diff --git a/.gitignore b/.gitignore
index ea69f5dd..8ca099cc 100755
--- a/.gitignore
+++ b/.gitignore
@@ -15,6 +15,7 @@
*.ipr
*.iws
.idea/
+.renderers/
.metadata
analysis_options.yaml
diff --git a/README.md b/README.md
index 6e58832a..0ec1838e 100644
--- a/README.md
+++ b/README.md
@@ -57,6 +57,7 @@ This is a dart conversion of three.js and three_dart, originally created by [@mr
- WebGL2 supported. please add `` to your index.html to load the js_interop file.
**Linux**
+ - Currenlty only works for flutter < 3.27
- Ubuntu supported (Tested on Linux Mint)
- OpenGL supported
@@ -69,6 +70,12 @@ Please use [Permission Handler](https://pub.dev/packages/permission_handler) pac
This project is a simple 3D rendering engine for flutter to view, edit, or manipulate 3D models.
+## Legacy
+
+Please use [three_js_webgl](https://pub.dev/packages/three_js_webgl) for three_dart type renderer.
+
+As this project moves twards impeller renderer google's ANGLE will be removed and this version will be moved to three_js_angle, which currenly does not exists.
+
## Example
Find the example for this API [here](https://github.com/Knightro63/three_js/tree/main/packages/three_js/example/), for more examples you can click [here](https://github.com/Knightro63/three_js/tree/main/examples/), and for a preview go [here](https://knightro63.github.io/three_js/).
diff --git a/examples/lib/camera/webgl_camera.dart b/examples/lib/camera/webgl_camera.dart
index 7b6c89f8..251506cb 100644
--- a/examples/lib/camera/webgl_camera.dart
+++ b/examples/lib/camera/webgl_camera.dart
@@ -44,7 +44,7 @@ class _MyAppState extends State {
void dispose() {
timer.cancel();
threeJs.dispose();
- threeJs.renderer!.setScissor( 0, 0, threeJs.width , threeJs.height);
+ threeJs.renderer?.setScissor( 0, 0, threeJs.width , threeJs.height);
super.dispose();
cameraPerspective.dispose();
diff --git a/examples/lib/clipping/webgl_clipping_stencil.dart b/examples/lib/clipping/webgl_clipping_stencil.dart
index 81f33465..669657d3 100644
--- a/examples/lib/clipping/webgl_clipping_stencil.dart
+++ b/examples/lib/clipping/webgl_clipping_stencil.dart
@@ -170,7 +170,7 @@ class _State extends State {
final po = three.Mesh(planeGeom, planeMat);
po.onAfterRender = ({
- three.WebGLRenderer? renderer,
+ three.Renderer? renderer,
three.RenderTarget? renderTarget,
three.Object3D? mesh,
three.Scene? scene,
diff --git a/examples/lib/geometry/webgl_geometry_spline_editor.dart b/examples/lib/geometry/webgl_geometry_spline_editor.dart
index 509c9de9..a601f30e 100644
--- a/examples/lib/geometry/webgl_geometry_spline_editor.dart
+++ b/examples/lib/geometry/webgl_geometry_spline_editor.dart
@@ -1,5 +1,6 @@
import 'dart:async';
import 'dart:math' as math;
+import 'dart:typed_data';
import 'package:example/src/gui.dart';
import 'package:flutter/material.dart';
import 'package:example/src/statistics.dart';
@@ -167,7 +168,7 @@ class _State extends State {
}
final geometry = three.BufferGeometry();
- geometry.setAttributeFromString( 'position', three.Float32BufferAttribute( three.Float32Array( ARC_SEGMENTS * 3 ), 3 ) );
+ geometry.setAttributeFromString( 'position', three.Float32BufferAttribute( Float32List( ARC_SEGMENTS * 3 ), 3 ) );
three.CatmullRomCurve3 curve = three.CatmullRomCurve3( points:positions );
three.Line mesh;
diff --git a/examples/lib/geometry/webgl_interactive_raycasting_points.dart b/examples/lib/geometry/webgl_interactive_raycasting_points.dart
index b9153b6b..17d0dd6a 100644
--- a/examples/lib/geometry/webgl_interactive_raycasting_points.dart
+++ b/examples/lib/geometry/webgl_interactive_raycasting_points.dart
@@ -1,5 +1,6 @@
import 'dart:async';
import 'dart:math' as math;
+import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:example/src/statistics.dart';
import 'package:three_js/three_js.dart' as three;
@@ -110,8 +111,8 @@ class _State extends State {
final geometry = three.BufferGeometry();
final numPoints = (width * length).toInt();
- final positions = three.Float32Array( numPoints * 3 );
- final colors = three.Float32Array( numPoints * 3 );
+ final positions = Float32List( numPoints * 3 );
+ final colors = Float32List( numPoints * 3 );
int k = 0;
@@ -153,7 +154,7 @@ class _State extends State {
three.Points generateIndexedPointcloud(three.Color color, int width, int length ) {
final geometry = generatePointCloudGeometry( color, width, length );
final numPoints = (width * length).toInt();
- final indices = three.Uint16Array( numPoints );
+ final indices = Uint16List( numPoints );
int k = 0;
@@ -172,7 +173,7 @@ class _State extends State {
three.Points generateIndexedWithOffsetPointcloud(three.Color color, int width, int length ) {
final geometry = generatePointCloudGeometry( color, width, length );
final numPoints = (width * length).toInt();
- final indices = three.Uint16Array( numPoints );
+ final indices = Uint16List( numPoints );
int k = 0;
diff --git a/examples/lib/lights/webgl_lightprobe_cube_camera.dart b/examples/lib/lights/webgl_lightprobe_cube_camera.dart
index ad0132c2..827c1999 100644
--- a/examples/lib/lights/webgl_lightprobe_cube_camera.dart
+++ b/examples/lib/lights/webgl_lightprobe_cube_camera.dart
@@ -60,7 +60,7 @@ class _State extends State {
threeJs.camera = three.PerspectiveCamera( 40, threeJs.width / threeJs.height, 1, 1000 );
threeJs.camera.position.setValues( 0, 0, 30 );
- final cubeRenderTarget = three.WebGLCubeRenderTarget( 256 );
+ final cubeRenderTarget = three.CubeRenderTarget( 256 );
final cubeCamera = three.CubeCamera( 1, 1000, cubeRenderTarget );
// controls
diff --git a/examples/lib/line/webgl_lines_fat.dart b/examples/lib/line/webgl_lines_fat.dart
index 109ba422..f82d98b0 100644
--- a/examples/lib/line/webgl_lines_fat.dart
+++ b/examples/lib/line/webgl_lines_fat.dart
@@ -1,4 +1,5 @@
import 'dart:async';
+import 'dart:typed_data';
import 'package:example/src/geometry_utils.dart';
import 'package:example/src/gui.dart';
import 'package:flutter/material.dart';
@@ -106,8 +107,8 @@ class _State extends State {
// Line2 ( LineGeometry, LineMaterial )
final geometry = LineGeometry();
- geometry.setPositions(three.Float32Array.fromList(positions));
- geometry.setColors(three.Float32Array.fromList(colors));
+ geometry.setPositions(Float32List.fromList(positions));
+ geometry.setColors(Float32List.fromList(colors));
matLine = LineMaterial.fromMap( {
'color': 0xffffff,
diff --git a/examples/lib/line/webgl_lines_fat_raycasting.dart b/examples/lib/line/webgl_lines_fat_raycasting.dart
index 7540331e..7cf68828 100644
--- a/examples/lib/line/webgl_lines_fat_raycasting.dart
+++ b/examples/lib/line/webgl_lines_fat_raycasting.dart
@@ -1,5 +1,6 @@
import 'dart:async';
import 'dart:math' as math;
+import 'dart:typed_data';
import 'package:example/src/gui.dart';
import 'package:flutter/material.dart';
import 'package:example/src/statistics.dart';
@@ -147,12 +148,12 @@ class _State extends State {
}
final lineGeometry = LineGeometry();
- lineGeometry.setPositions(three.Float32Array.fromList(positions));
- lineGeometry.setColors(three.Float32Array.fromList(colors));
+ lineGeometry.setPositions(Float32List.fromList(positions));
+ lineGeometry.setColors(Float32List.fromList(colors));
final segmentsGeometry = LineSegmentsGeometry();
- segmentsGeometry.setPositions(three.Float32Array.fromList(positions));
- segmentsGeometry.setColors(three.Float32Array.fromList(colors));
+ segmentsGeometry.setPositions(Float32List.fromList(positions));
+ segmentsGeometry.setColors(Float32List.fromList(colors));
segments = LineSegments2( segmentsGeometry, matLine );
segments.computeLineDistances();
diff --git a/examples/lib/loaders/webgl_loader_glb.dart b/examples/lib/loaders/webgl_loader_glb.dart
index d159f13a..1da2aa1c 100644
--- a/examples/lib/loaders/webgl_loader_glb.dart
+++ b/examples/lib/loaders/webgl_loader_glb.dart
@@ -84,6 +84,7 @@ class _MyAppState extends State {
three.GLTFData? result = await loader.fromAsset( 'dash.glb' );
final object = result!.scene;
+ object.frustumCulled = false;
threeJs.scene.add(object);
mixer = three.AnimationMixer(object);
mixer.clipAction(result.animations![4], null, null)!.play();
diff --git a/examples/lib/loaders/webgl_loader_svg.dart b/examples/lib/loaders/webgl_loader_svg.dart
index bb14af26..3eacd8d3 100644
--- a/examples/lib/loaders/webgl_loader_svg.dart
+++ b/examples/lib/loaders/webgl_loader_svg.dart
@@ -133,8 +133,9 @@ class _MyAppState extends State {
final strokeColor = path.userData?["style"]["stroke"];
if (guiData["drawStrokes"] == true &&
- strokeColor != null &&
- strokeColor != 'none') {
+ strokeColor != null &&
+ strokeColor != 'none'
+ ) {
three.MeshBasicMaterial material = three.MeshBasicMaterial.fromMap({
"color": three.Color().setStyle(strokeColor).convertSRGBToLinear(),
"opacity": path.userData?["style"]["strokeOpacity"],
@@ -146,8 +147,7 @@ class _MyAppState extends State {
for (int j = 0, jl = path.subPaths.length; j < jl; j++) {
three.Path subPath = path.subPaths[j];
- final geometry = SVGLoader.pointsToStroke(
- subPath.getPoints(), path.userData?["style"]);
+ final geometry = SVGLoader.pointsToStroke(subPath.getPoints(), path.userData?["style"]);
if (geometry != null) {
final mesh = three.Mesh(geometry, material);
diff --git a/examples/lib/material/webgl_materials.dart b/examples/lib/material/webgl_materials.dart
index 6b74f725..f7fb68b1 100644
--- a/examples/lib/material/webgl_materials.dart
+++ b/examples/lib/material/webgl_materials.dart
@@ -1,5 +1,6 @@
import 'dart:async';
import 'dart:math' as math;
+import 'dart:typed_data';
import 'package:example/src/statistics.dart';
import 'package:flutter/material.dart';
import 'package:three_js/three_js.dart' as three;
@@ -144,7 +145,7 @@ class _MyAppState extends State {
}
three.ImageElement generateTexture() {
- final pixels = three.Uint8Array(256 * 256 * 4);
+ final pixels = Uint8List(256 * 256 * 4);
int x = 0, y = 0, l = pixels.length;
diff --git a/examples/lib/material/webgl_materials_physical_transmission.dart b/examples/lib/material/webgl_materials_physical_transmission.dart
index 7c6c46ca..42bdc44d 100644
--- a/examples/lib/material/webgl_materials_physical_transmission.dart
+++ b/examples/lib/material/webgl_materials_physical_transmission.dart
@@ -1,5 +1,6 @@
import 'dart:async';
import 'dart:math' as math;
+import 'dart:typed_data';
import 'package:example/src/gui.dart';
import 'package:flutter/material.dart';
import 'package:example/src/statistics.dart';
@@ -208,7 +209,7 @@ class _State extends State {
return three.ImageElement(
width: 2,
height: 2,
- data: three.Uint8Array.fromList([0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255])
+ data: Uint8List.fromList([0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255])
);
}
}
diff --git a/examples/lib/material/webgl_materials_video.dart b/examples/lib/material/webgl_materials_video.dart
index e5872dd0..ad50c987 100644
--- a/examples/lib/material/webgl_materials_video.dart
+++ b/examples/lib/material/webgl_materials_video.dart
@@ -4,8 +4,6 @@ import 'package:flutter/material.dart';
import 'package:example/src/statistics.dart';
import 'package:three_js/three_js.dart' as three;
import 'package:three_js_postprocessing/post/effect_composer.dart';
-// import 'package:three_js_postprocessing/post/bloom_pass.dart';
-// import 'package:three_js_postprocessing/post/outpass.dart';
import 'package:three_js_postprocessing/post/render_pass.dart';
import 'package:three_js_video_texture/three_js_video_texture.dart';
diff --git a/examples/lib/mirror/webgl_portal.dart b/examples/lib/mirror/webgl_portal.dart
index 9061ea45..d1857456 100644
--- a/examples/lib/mirror/webgl_portal.dart
+++ b/examples/lib/mirror/webgl_portal.dart
@@ -97,14 +97,14 @@ class _State extends State {
final topLeftCorner = three.Vector3();
final reflectedPosition = three.Vector3();
- final leftPortalTexture = three.WebGLRenderTarget( 256, 256 );
+ final leftPortalTexture = three.RenderTarget( 256, 256 );
final leftPortal = three.Mesh( planeGeo, three.MeshBasicMaterial.fromMap( { 'map': leftPortalTexture.texture } ) );
leftPortal.position.x = - 30;
leftPortal.position.y = 20;
leftPortal.scale.setValues( 0.35, 0.35, 0.35 );
threeJs.scene.add( leftPortal );
- final rightPortalTexture = three.WebGLRenderTarget( 256, 256 );
+ final rightPortalTexture = three.RenderTarget( 256, 256 );
final rightPortal = three.Mesh( planeGeo, three.MeshBasicMaterial.fromMap( { 'map': rightPortalTexture.texture } ) );
rightPortal.position.x = 30;
rightPortal.position.y = 20;
@@ -162,7 +162,7 @@ class _State extends State {
blueLight.position.setValues( 0, 50, 550 );
threeJs.scene.add( blueLight );
- void renderPortal(three.Mesh thisPortalMesh,three.Mesh otherPortalMesh,three.WebGLRenderTarget thisPortalTexture ) {
+ void renderPortal(three.Mesh thisPortalMesh,three.Mesh otherPortalMesh,three.RenderTarget thisPortalTexture ) {
thisPortalMesh.worldToLocal( reflectedPosition.setFrom( threeJs.camera.position ) );
reflectedPosition.x *= - 1.0; reflectedPosition.z *= - 1.0;
otherPortalMesh.localToWorld( reflectedPosition );
diff --git a/examples/lib/modifers/webgl_modifer_tessellation.dart b/examples/lib/modifers/webgl_modifer_tessellation.dart
index 613027ff..4a597ae1 100644
--- a/examples/lib/modifers/webgl_modifer_tessellation.dart
+++ b/examples/lib/modifers/webgl_modifer_tessellation.dart
@@ -1,5 +1,6 @@
import 'dart:async';
import 'dart:math' as math;
+import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:example/src/statistics.dart';
import 'package:three_js/three_js.dart' as three;
@@ -80,8 +81,8 @@ class _State extends State {
final geometry = tessellateModifier.modify( textG );
final numFaces = geometry.attributes['position'].count ~/ 3;
- final colors = three.Float32Array( numFaces * 3 * 3 );
- final displacement = three.Float32Array( numFaces * 3 * 3 );
+ final colors = Float32List( numFaces * 3 * 3 );
+ final displacement = Float32List( numFaces * 3 * 3 );
final color = three.Color();
for (int f = 0; f < numFaces; f ++ ) {
diff --git a/examples/lib/multi_views/multi_views.dart b/examples/lib/multi_views/multi_views.dart
index 344ac7ce..43078316 100644
--- a/examples/lib/multi_views/multi_views.dart
+++ b/examples/lib/multi_views/multi_views.dart
@@ -1,6 +1,7 @@
import 'dart:async';
import 'package:flutter/material.dart' hide Matrix4;
import 'package:three_js/three_js.dart' as three;
+import 'package:flutter_angle/flutter_angle.dart';
class MultiViews extends StatefulWidget {
const MultiViews({super.key});
@@ -9,7 +10,7 @@ class MultiViews extends StatefulWidget {
}
class _MyAppState extends State {
- List textures = [];
+ List textures = [];
ScrollController controller = ScrollController();
@override
diff --git a/examples/lib/multi_views/webgl2_multiple_rendertargets.dart b/examples/lib/multi_views/webgl2_multiple_rendertargets.dart
index 34f2fccb..dac17a27 100644
--- a/examples/lib/multi_views/webgl2_multiple_rendertargets.dart
+++ b/examples/lib/multi_views/webgl2_multiple_rendertargets.dart
@@ -169,10 +169,10 @@ class _State extends State {
Future setup() async {
// Create a multi render target with Float buffers
- final renderTarget = three.WebGLRenderTarget(
+ final renderTarget = three.RenderTarget(
(threeJs.width * threeJs.dpr).toInt(),
(threeJs.height * threeJs.dpr).toInt(),
- three.WebGLRenderTargetOptions({
+ three.RenderTargetOptions({
'count': 2,
'minFilter': three.NearestFilter,
'magFilter': three.NearestFilter
diff --git a/examples/lib/multi_views/webgl_multi_views.dart b/examples/lib/multi_views/webgl_multi_views.dart
index 78a85aaa..83255214 100644
--- a/examples/lib/multi_views/webgl_multi_views.dart
+++ b/examples/lib/multi_views/webgl_multi_views.dart
@@ -1,5 +1,6 @@
import 'dart:async';
import 'dart:math' as math;
+import 'dart:typed_data';
import 'package:example/src/statistics.dart';
import 'package:flutter/material.dart';
import 'package:three_js/three_js.dart' as three;
@@ -156,7 +157,7 @@ class _MyAppState extends State {
final geometry1 = IcosahedronGeometry( radius, 1 );
final count = geometry1.attributes['position'].count;
- geometry1.setAttributeFromString( 'color', three.Float32BufferAttribute( three.Float32Array( count * 3 ), 3 ) );
+ geometry1.setAttributeFromString( 'color', three.Float32BufferAttribute( Float32List( count * 3 ), 3 ) );
final geometry2 = geometry1.clone();
final geometry3 = geometry1.clone();
diff --git a/examples/lib/others/webgl_buffergeometry_instancing_billboards.dart b/examples/lib/others/webgl_buffergeometry_instancing_billboards.dart
index 0ac868fb..5d85b18a 100644
--- a/examples/lib/others/webgl_buffergeometry_instancing_billboards.dart
+++ b/examples/lib/others/webgl_buffergeometry_instancing_billboards.dart
@@ -68,7 +68,7 @@ class _State extends State {
const particleCount = 75000;
- final translateArray = three.Float32Array( particleCount * 3 );
+ final translateArray = Float32List( particleCount * 3 );
for ( int i = 0, i3 = 0, l = particleCount; i < l; i ++, i3 += 3 ) {
translateArray[ i3 + 0 ] = math.Random().nextDouble() * 2 - 1;
diff --git a/examples/lib/others/webgl_custom_attributes_lines.dart b/examples/lib/others/webgl_custom_attributes_lines.dart
index 7c0ef0cf..49f1337d 100644
--- a/examples/lib/others/webgl_custom_attributes_lines.dart
+++ b/examples/lib/others/webgl_custom_attributes_lines.dart
@@ -1,5 +1,6 @@
import 'dart:async';
import 'dart:math' as math;
+import 'dart:typed_data';
import 'package:example/src/statistics.dart';
import 'package:flutter/material.dart';
import 'package:three_js/three_js.dart' as three;
@@ -120,17 +121,17 @@ class _State extends State {
geometry.center();
final count = geometry.attributes['position'].count;
- final displacement = three.Float32BufferAttribute(three.Float32Array( count * 3), 3 );
+ final displacement = three.Float32BufferAttribute(Float32List( count * 3), 3 );
geometry.setAttributeFromString( 'displacement', displacement );
- final customColor = three.Float32BufferAttribute(three.Float32Array( count * 3), 3 );
+ final customColor = three.Float32BufferAttribute(Float32List( count * 3), 3 );
geometry.setAttributeFromString( 'customColor', customColor );
final color = three.Color( 0xffffff );
for (int i = 0, l = customColor.count; i < l; i ++ ) {
color.setHSL( i / l, 0.5, 0.5 );
- color.copyIntoArray( customColor.array, i * customColor.itemSize );
+ color.copyIntoList( customColor.array, i * customColor.itemSize );
}
final line = three.Line( geometry, shaderMaterial );
diff --git a/examples/lib/postprocessing/webgl_postprocessing_fxaa.dart b/examples/lib/postprocessing/webgl_postprocessing_fxaa.dart
index c456bb0c..2de035b5 100644
--- a/examples/lib/postprocessing/webgl_postprocessing_fxaa.dart
+++ b/examples/lib/postprocessing/webgl_postprocessing_fxaa.dart
@@ -4,6 +4,7 @@ import 'package:example/src/statistics.dart';
import 'package:three_js/three_js.dart' as three;
import 'dart:math' as math;
import 'package:three_js_postprocessing/three_js_postprocessing.dart';
+import 'package:flutter_angle/flutter_angle.dart';
class WebglPostprocessingFXAA extends StatefulWidget {
const WebglPostprocessingFXAA({super.key});
@@ -119,7 +120,7 @@ class _State extends State {
threeJs.customRenderer = renderer;
}
- Future renderer(three.Scene scene, three.Camera camera, three.FlutterAngleTexture texture,[dt]) async{
+ Future renderer(three.Scene scene, three.Camera camera, FlutterAngleTexture texture,[dt]) async{
final halfWidth = threeJs.width / 2;
controls.update();
diff --git a/examples/lib/projected/webgl_projected_basic.dart b/examples/lib/projected/webgl_projected_basic.dart
index 3c0f028b..52f80fe6 100644
--- a/examples/lib/projected/webgl_projected_basic.dart
+++ b/examples/lib/projected/webgl_projected_basic.dart
@@ -1,5 +1,6 @@
import 'dart:async';
import 'dart:math' as math;
+import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:example/src/statistics.dart';
import 'package:three_js/three_js.dart' as three;
@@ -64,7 +65,7 @@ class _State extends State {
threeJs.camera.lookAt(three.Vector3(0, 0, 0));
// load the example texture
- final texture = await three.TextureLoader().fromAsset('assets/textures/uv_grid_directx.jpg');
+ final texture = await three.TextureLoader(flipY: !kIsWeb).fromAsset('assets/textures/uv_grid_directx.jpg');
final camera = three.PerspectiveCamera(45, 1, 0.01, 3);
camera.position.setValues(-1, 1.2, 2);
camera.lookAt(three.Vector3(0, 0, 0));
@@ -82,6 +83,7 @@ class _State extends State {
);
final box = three.Mesh(geometry, material);
threeJs.scene.add(box);
+ material.needsUpdate = true;
// move the mesh any way you want!
box.rotation.y = -math.pi / 4;
@@ -93,6 +95,11 @@ class _State extends State {
final ambientLight = three.AmbientLight(0xffffff, 0.8);
threeJs.scene.add(ambientLight);
+ final dirLight = three.DirectionalLight( 0xFFFFFF, 3 );
+ dirLight.position.setValues( - 0.5, 1, 0.8 );
+ dirLight.castShadow = true;
+ threeJs.scene.add( dirLight );
+
controls = three.OrbitControls(threeJs.camera, threeJs.globalKey);
}
}
diff --git a/examples/lib/rollercoster/rollercoaster.dart b/examples/lib/rollercoster/rollercoaster.dart
index 4f82152e..db8e4f01 100644
--- a/examples/lib/rollercoster/rollercoaster.dart
+++ b/examples/lib/rollercoster/rollercoaster.dart
@@ -1,4 +1,5 @@
import 'dart:math' as math;
+import 'dart:typed_data';
import 'package:three_js/three_js.dart';
@@ -198,9 +199,9 @@ class RollerCoasterGeometry extends BufferGeometry {
// console.log( vertices.length );
- this.setAttributeFromString( 'position', Float32BufferAttribute( Float32Array.fromList( vertices ), 3 ) );
- this.setAttributeFromString( 'normal', Float32BufferAttribute( Float32Array.fromList( normals ), 3 ) );
- this.setAttributeFromString( 'color', Float32BufferAttribute( Float32Array.fromList( colors ), 3 ) );
+ this.setAttributeFromString( 'position', Float32BufferAttribute( Float32List.fromList( vertices ), 3 ) );
+ this.setAttributeFromString( 'normal', Float32BufferAttribute( Float32List.fromList( normals ), 3 ) );
+ this.setAttributeFromString( 'color', Float32BufferAttribute( Float32List.fromList( colors ), 3 ) );
}
}
@@ -363,8 +364,8 @@ class RollerCoasterLiftersGeometry extends BufferGeometry {
}
}
- this.setAttributeFromString( 'position', Float32BufferAttribute( Float32Array.fromList( vertices ), 3 ) );
- this.setAttributeFromString( 'normal', Float32BufferAttribute( Float32Array.fromList( normals ), 3 ) );
+ this.setAttributeFromString( 'position', Float32BufferAttribute( Float32List.fromList( vertices ), 3 ) );
+ this.setAttributeFromString( 'normal', Float32BufferAttribute( Float32List.fromList( normals ), 3 ) );
}
}
@@ -429,7 +430,7 @@ class RollerCoasterShadowGeometry extends BufferGeometry {
prevQuaternion.setFrom( quaternion );
}
- this.setAttributeFromString( 'position', Float32BufferAttribute( Float32Array.fromList( vertices ), 3 ) );
+ this.setAttributeFromString( 'position', Float32BufferAttribute( Float32List.fromList( vertices ), 3 ) );
}
}
@@ -455,7 +456,7 @@ class SkyGeometry extends BufferGeometry {
vertices.addAll([ x - size, y, z + size ]);
}
- this.setAttributeFromString( 'position', Float32BufferAttribute( Float32Array.fromList( vertices ), 3 ) );
+ this.setAttributeFromString( 'position', Float32BufferAttribute( Float32List.fromList( vertices ), 3 ) );
}
}
@@ -504,8 +505,8 @@ class TreesGeometry extends BufferGeometry {
}
}
- this.setAttributeFromString( 'position', Float32BufferAttribute( Float32Array.fromList( vertices ), 3 ) );
- this.setAttributeFromString( 'color', Float32BufferAttribute( Float32Array.fromList( colors ), 3 ) );
+ this.setAttributeFromString( 'position', Float32BufferAttribute( Float32List.fromList( vertices ), 3 ) );
+ this.setAttributeFromString( 'color', Float32BufferAttribute( Float32List.fromList( colors ), 3 ) );
}
diff --git a/examples/lib/rollercoster/webxr_vr_rollercoaster.dart b/examples/lib/rollercoster/webxr_vr_rollercoaster.dart
index d5c17564..afb1f049 100644
--- a/examples/lib/rollercoster/webxr_vr_rollercoaster.dart
+++ b/examples/lib/rollercoster/webxr_vr_rollercoaster.dart
@@ -1,5 +1,6 @@
import 'dart:async';
import 'dart:math' as math;
+import 'dart:typed_data';
import 'package:example/rollercoster/rollercoaster.dart';
import 'package:flutter/material.dart';
import 'package:example/src/statistics.dart';
@@ -7,7 +8,7 @@ import 'package:three_js/three_js.dart' as three;
import 'package:three_js_geometry/three_js_geometry.dart';
extension on three.Vector3{
- three.Float32Array toNativeArray(three.Float32Array array, [int offset = 0]) {
+ Float32List toNativeArray(Float32List array, [int offset = 0]) {
array[offset] = storage[0];
array[offset + 1] = storage[1];
array[offset + 2] = storage[2];
@@ -94,7 +95,7 @@ class _State extends State {
final vertex = three.Vector3();
for (int i = 0; i < positions.length; i += 3 ) {
- vertex.fromNativeArray( positions, i );
+ vertex.fromArray( positions, i );
vertex.x += math.Random().nextDouble() * 10 - 5;
vertex.z += math.Random().nextDouble() * 10 - 5;
diff --git a/examples/lib/screenshot/opengl_screenshot.dart b/examples/lib/screenshot/opengl_screenshot.dart
index ef6b2968..5f250d5a 100644
--- a/examples/lib/screenshot/opengl_screenshot.dart
+++ b/examples/lib/screenshot/opengl_screenshot.dart
@@ -16,8 +16,8 @@ class _State extends State {
List data = List.filled(60, 0, growable: true);
late Timer timer;
late three.ThreeJS threeJs;
- late final three.Uint8Array buffer;
- late final three.WebGLRenderTarget rt;
+ late final Uint8List buffer;
+ late final three.RenderTarget rt;
@override
void initState() {
@@ -54,7 +54,7 @@ class _State extends State {
img.Image image = img.Image.fromBytes(
width: desiredWidth,
height: desiredHeight,
- bytes: buffer.toDartList().buffer,
+ bytes: buffer.buffer,
numChannels: 4,
order: img.ChannelOrder.rgb
);
@@ -89,8 +89,8 @@ class _State extends State {
int desiredHeight = 1080;
Future setup() async {
- buffer = three.Uint8Array( desiredWidth * desiredHeight * 4 );
- rt = three.WebGLRenderTarget( desiredWidth, desiredHeight, three.WebGLRenderTargetOptions({
+ buffer = Uint8List( desiredWidth * desiredHeight * 4 );
+ rt = three.RenderTarget( desiredWidth, desiredHeight, three.RenderTargetOptions({
'colorSpace': three.SRGBColorSpace,
'samples': 4,
}) );
diff --git a/examples/lib/shadow/webgl_shadow_contact.dart b/examples/lib/shadow/webgl_shadow_contact.dart
index cfddd2f8..a24e8558 100644
--- a/examples/lib/shadow/webgl_shadow_contact.dart
+++ b/examples/lib/shadow/webgl_shadow_contact.dart
@@ -52,8 +52,8 @@ class _MyAppState extends State {
}
late three.Mesh blurPlane;
- late three.WebGLRenderTarget renderTarget;
- late three.WebGLRenderTarget renderTargetBlur;
+ late three.RenderTarget renderTarget;
+ late three.RenderTarget renderTargetBlur;
final meshes = [];
@@ -210,13 +210,13 @@ class _MyAppState extends State {
shadowGroup.position.y = -0.3;
threeJs.scene.add(shadowGroup);
- final pars = three.WebGLRenderTargetOptions({"format": three.RGBAFormat});
+ final pars = three.RenderTargetOptions({"format": three.RGBAFormat});
// the render target that will show the shadows in the plane texture
- renderTarget = three.WebGLRenderTarget(512, 512, pars);
+ renderTarget = three.RenderTarget(512, 512, pars);
renderTarget.texture.generateMipmaps = false;
// the render target that we will use to blur the first render target
- renderTargetBlur = three.WebGLRenderTarget(512, 512, pars);
+ renderTargetBlur = three.RenderTarget(512, 512, pars);
renderTargetBlur.texture.generateMipmaps = false;
// make a plane and make it face up
diff --git a/examples/lib/shadow/webgl_shadowmap_pointlight.dart b/examples/lib/shadow/webgl_shadowmap_pointlight.dart
index 178c705d..c5966ed4 100644
--- a/examples/lib/shadow/webgl_shadowmap_pointlight.dart
+++ b/examples/lib/shadow/webgl_shadowmap_pointlight.dart
@@ -1,5 +1,6 @@
import 'dart:async';
import 'dart:math' as math;
+import 'dart:typed_data';
import 'package:example/src/statistics.dart';
import 'package:flutter/material.dart';
import 'package:three_js/three_js.dart' as three;
@@ -153,7 +154,7 @@ class _State extends State {
return three.ImageElement(
width: 2,
height: 2,
- data: three.Uint8Array.fromList([0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255])
+ data: Uint8List.fromList([0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255])
);
}
}
diff --git a/examples/lib/shadow/webgl_simple_gi.dart b/examples/lib/shadow/webgl_simple_gi.dart
index 776ae899..a37505d6 100644
--- a/examples/lib/shadow/webgl_simple_gi.dart
+++ b/examples/lib/shadow/webgl_simple_gi.dart
@@ -98,7 +98,7 @@ class _State extends State {
void Function([double?])? compute;
- void simpleGI(three.WebGLRenderer renderer, three.Scene scene) {
+ void simpleGI(three.Renderer renderer, three.Scene scene) {
final SIZE = 32, SIZE2 = SIZE * SIZE;
@@ -109,7 +109,7 @@ class _State extends State {
three.Object3D clone = scene.clone();
clone.matrixAutoUpdate = false;
- final rt = three.WebGLRenderTarget( SIZE, SIZE );
+ final rt = three.RenderTarget( SIZE, SIZE );
final normalMatrix = three.Matrix3.identity();
final position = three.Vector3();
@@ -119,7 +119,7 @@ class _State extends State {
int currentVertex = 0;
final color = Float32List( 3 );
- final three.Uint8Array buffer = three.Uint8Array( SIZE2 * 4 );
+ final Uint8List buffer = Uint8List( SIZE2 * 4 );
void compute([double? dt]) {
if ( bounces == 3 ) return;
@@ -143,10 +143,10 @@ class _State extends State {
for (int i = 0; i < 32; i ++ ) {
if ( currentVertex >= totalVertex ) break;
- position.fromNativeArray( positions, currentVertex * 3 );
+ position.fromArray( positions, currentVertex * 3 );
position.applyMatrix4( object.matrixWorld );
- normal.fromNativeArray( normals, currentVertex * 3 );
+ normal.fromArray( normals, currentVertex * 3 );
normal.applyMatrix3( normalMatrix.getNormalMatrix( object.matrixWorld ) ).normalize();
camera.position.setFrom( position );
diff --git a/examples/lib/terrain/three_terrain.dart b/examples/lib/terrain/three_terrain.dart
index 70f031c9..b4f9955b 100644
--- a/examples/lib/terrain/three_terrain.dart
+++ b/examples/lib/terrain/three_terrain.dart
@@ -154,7 +154,7 @@ class _State extends State {
applySmoothing(val, lastOptions);
scatterMeshes();
if (lastOptions.heightmap != null) {
- terrain.Terrain.toHeightmap(terrainScene!.children[0].geometry!.attributes['position'].array.toDartList(), lastOptions);
+ terrain.Terrain.toHeightmap(terrainScene!.children[0].geometry!.attributes['position'].array, lastOptions);
}
})
..addSlider(guiSettings,'segments', 7, 127).onFinishChange((){regenerate(blend);})
@@ -216,7 +216,7 @@ class _State extends State {
void applySmoothing(smoothing, terrain.TerrainOptions o) {
three.Object3D m = terrainScene!.children[0];
- Float32List g = terrain.Terrain.toArray1D(m.geometry!.attributes['position'].array.toDartList());
+ Float32List g = terrain.Terrain.toArray1D(m.geometry!.attributes['position'].array);
if (smoothing == 'Conservative (0.5)') terrain.Terrain.smoothConservative(g, o, 0.5);
if (smoothing == 'Conservative (1)') terrain.Terrain.smoothConservative(g, o, 1);
if (smoothing == 'Conservative (10)'){ terrain.Terrain.smoothConservative(g, o, 10);}
@@ -230,7 +230,7 @@ class _State extends State {
else if (smoothing == 'Mean (1)'){ terrain.Terrain.smooth(g, o, 1);}
else if (smoothing == 'Mean (8)'){ terrain.Terrain.smooth(g, o, 8);}
else if (smoothing == 'Median'){ terrain.Terrain.smoothMedian(g, o);}
- terrain.Terrain.fromArray1D(m.geometry!.attributes['position'].array.toDartList(), g);
+ terrain.Terrain.fromArray1D(m.geometry!.attributes['position'].array, g);
terrain.Terrain.normalize(m, o);
}
@@ -441,7 +441,7 @@ class _State extends State {
// var he = document.getElementById('heightmap');
// if (he != null) {
// o.heightmap = he;
- heightMapImage = terrain.Terrain.toHeightmap(terrainScene!.children[0].geometry!.attributes['position'].array.toDartList(), o);
+ heightMapImage = terrain.Terrain.toHeightmap(terrainScene!.children[0].geometry!.attributes['position'].array, o);
// }
heightMapImage = rgba2bitmap(heightMapImage!, o.xSegments+1, o.ySegments+1);
lastOptions = o;
diff --git a/examples/lib/terrain/webgl_geometry_terrain.dart b/examples/lib/terrain/webgl_geometry_terrain.dart
index c241d6fd..eec073a4 100644
--- a/examples/lib/terrain/webgl_geometry_terrain.dart
+++ b/examples/lib/terrain/webgl_geometry_terrain.dart
@@ -1,5 +1,6 @@
import 'dart:async';
import 'dart:math' as math;
+import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:example/src/statistics.dart';
import 'package:three_js/three_js.dart' as three;
@@ -97,7 +98,7 @@ class _State extends State {
});
}
- three.Uint8Array generateHeight(int width,int height ) {
+ Uint8List generateHeight(int width,int height ) {
double seed = math.pi / 4;
double random() {
final x = math.sin( seed ++ ) * 10000;
@@ -105,7 +106,7 @@ class _State extends State {
}
int size = width * height;
- final data = three.Uint8Array( size );
+ final data = Uint8List( size );
final perlin = ImprovedNoise();
double z = random() * 100;
@@ -124,12 +125,12 @@ class _State extends State {
return data;
}
- three.ImageElement generateTexture(three.Uint8Array data,int width,int height ) {
+ three.ImageElement generateTexture(Uint8List data,int width,int height ) {
final vector3 = three.Vector3( 0, 0, 0 );
final sun = three.Vector3( 1, 1, 1 );
sun.normalize();
- final imageData = three.Uint8Array.fromList(List.filled(width*height*4, 255));
+ final imageData = Uint8List.fromList(List.filled(width*height*4, 255));
for (int i = 0, j = 0; i < imageData.length; i += 4, j ++ ) {
vector3.x = data[ j - 2 ] - data[ j + 2 ] *1.0;
diff --git a/examples/lib/terrain/webgl_geometry_terrain_raycast.dart b/examples/lib/terrain/webgl_geometry_terrain_raycast.dart
index c845377a..a1dafb9e 100644
--- a/examples/lib/terrain/webgl_geometry_terrain_raycast.dart
+++ b/examples/lib/terrain/webgl_geometry_terrain_raycast.dart
@@ -1,5 +1,6 @@
import 'dart:async';
import 'dart:math' as math;
+import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:example/src/statistics.dart';
import 'package:three_js/three_js.dart' as three;
@@ -111,7 +112,7 @@ class _State extends State {
threeJs.domElement.addEventListener(three.PeripheralType.pointerHover, onPointerMove );
}
- three.Uint8Array generateHeight(int width,int height ) {
+ Uint8List generateHeight(int width,int height ) {
double seed = math.pi / 4;
double random() {
final x = math.sin( seed ++ ) * 10000;
@@ -119,7 +120,7 @@ class _State extends State {
}
int size = width * height;
- final data = three.Uint8Array( size );
+ final data = Uint8List( size );
final perlin = ImprovedNoise();
double z = random() * 100;
@@ -138,12 +139,12 @@ class _State extends State {
return data;
}
- three.ImageElement generateTexture(three.Uint8Array data,int width,int height ) {
+ three.ImageElement generateTexture(Uint8List data,int width,int height ) {
final vector3 = three.Vector3( 0, 0, 0 );
final sun = three.Vector3( 1, 1, 1 );
sun.normalize();
- final imageData = three.Uint8Array.fromList(List.filled(width*height*4, 255));
+ final imageData = Uint8List.fromList(List.filled(width*height*4, 255));
for (int i = 0, j = 0; i < imageData.length; i += 4, j ++ ) {
vector3.x = data[ j - 2 ] - data[ j + 2 ] *1.0;
diff --git a/examples/lib/terrain/webgl_planet_generator.dart b/examples/lib/terrain/webgl_planet_generator.dart
index cc0a07af..dc290e8b 100644
--- a/examples/lib/terrain/webgl_planet_generator.dart
+++ b/examples/lib/terrain/webgl_planet_generator.dart
@@ -3,7 +3,6 @@ import 'package:example/src/gui.dart';
import 'package:flutter/material.dart';
import 'package:example/src/statistics.dart';
import 'package:three_js/three_js.dart' as three;
-import 'package:three_js_postprocessing/three_js_postprocessing.dart';
import 'package:three_js_objects/three_js_objects.dart';
class WebglPlanetGenerator extends StatefulWidget {
@@ -80,36 +79,27 @@ class _State extends State {
final controls = three.OrbitControls(threeJs.camera, threeJs.globalKey);
threeJs.camera.position.z = 50;
- final composer = EffectComposer(threeJs.renderer!);
- final renderPass = RenderPass(threeJs.scene, threeJs.camera);
- composer.addPass(renderPass);
-
- final bloomPass = UnrealBloomPass(null,0.0,0.2,0.5);
- composer.addPass(bloomPass);
-
- final outputPass = new OutputPass();
- composer.addPass(outputPass);
-
final texLoader = three.TextureLoader();
final cloudTex = await texLoader.fromAsset('assets/textures/planet_generator/cloud.png');
- PlanetGenerator planet = PlanetGenerator(cloudTexture: cloudTex);
+ PlanetGenerator planet = PlanetGenerator(
+ atmosphere: Atmosphere(cloudTexture: cloudTex)
+ );
threeJs.scene.add(planet);
threeJs.addAnimationEvent((dt) {
- planet.atmosphere.material?.uniforms['time']['value'] = threeJs.clock.getElapsedTime();
- planet.atmosphere.rotation.y += 0.0002;
+ planet.atmosphere?.material?.uniforms['time']['value'] += dt;
+ planet.atmosphere?.rotation.y += 0.0002;
controls.update();
- composer.render();
});
- createUI(planet, bloomPass);
+ createUI(planet);
}
- void createUI(PlanetGenerator planet,UnrealBloomPass bloomPass) {
+ void createUI(PlanetGenerator planet) {
final planetParams = planet.planetParams.json;
- final atmosphereParams = planet.atmosphereParams.json;
- Atmosphere atmosphere = planet.atmosphere;
+ final atmosphereParams = planet.atmosphereParams?.json;
+ Atmosphere? atmosphere = planet.atmosphere;
final terrainFolder = gui.addFolder('Terrain')..open();
terrainFolder.onChange((name,value){
@@ -122,13 +112,13 @@ class _State extends State {
else{
planet.material?.uniforms[name]['value'] = value;
}
- atmosphere.update();
+ atmosphere?.update(0.0);
});
terrainFolder.addDropDown(planetParams, 'type', ['1', '2', '3'])..name = 'Type';
- terrainFolder.addSlider(planetParams, 'amplitude', 0.1, 1.5,0.1)..name ='Amplitude';
+ terrainFolder.addSlider(planetParams, 'amplitude', 0.01, 1.5,0.01)..name ='Amplitude';
terrainFolder.addSlider(planetParams, 'sharpness', 0, 5,0.1)..name = 'Sharpness';
terrainFolder.addSlider(planetParams, 'offset', -2, 2,0.1)..name = 'Offset';
- terrainFolder.addSlider(planetParams, 'period', 0.1, 2,0.1)..name = 'Period';
+ terrainFolder.addSlider(planetParams, 'period', 0.1, 3,0.1)..name = 'Period';
terrainFolder.addSlider(planetParams, 'persistence', 0, 1,0.1)..name = 'Persistence';
terrainFolder.addSlider(planetParams, 'lacunarity', 1, 3,0.1)..name = 'Lacunarity';
terrainFolder.addSlider(planetParams, 'octaves', 1, 10, 1)..name = 'Octaves';
@@ -192,34 +182,36 @@ class _State extends State {
layer5Folder.addSlider(planetParams, 'blend45', 0, 1, 0.1)..name = 'Blend Factor (4->5)';
layer5Folder.addColor(planetParams, 'color5')..name = 'Color';
- final atmosphereFolder = gui.addFolder('Atmosphere')..open()..onChange((name,value){
- if(name == 'Atmosphere'){
- planet.atmosphere.material?.uniforms['speed']['value'] = value;
- }
- else{
- if(name == 'particles'||name == 'minParticleSize'||name == 'maxParticleSize'){
- planet.atmosphere.material?.uniforms[name]['value'] = value.toInt();
+ if(planet.atmosphere != null){
+ final atmosphereFolder = gui.addFolder('Atmosphere')..open()..onChange((name,value){
+ if(name == 'Atmosphere'){
+ planet.atmosphere?.material?.uniforms['speed']['value'] = value;
}
else{
- planet.atmosphere.material?.uniforms[name]['value'] = value;
+ if(name == 'particles'||name == 'minParticleSize'||name == 'maxParticleSize'){
+ planet.atmosphere?.material?.uniforms[name]['value'] = value.toInt();
+ }
+ else{
+ planet.atmosphere?.material?.uniforms[name]['value'] = value;
+ }
}
- }
- atmosphere.update();
- });
- atmosphereFolder.addSlider(atmosphereParams, 'thickness', 0.1, 5, 0.1)..name = 'Thickness';
- atmosphereFolder.addSlider(atmosphereParams, 'particles', 1, 50000, 1)..name = 'Particle Count';
- atmosphereFolder.addSlider(atmosphereParams, 'minParticleSize', 0, 200)..name = 'Min Particle Size';
- atmosphereFolder.addSlider(atmosphereParams, 'maxParticleSize', 0, 200)..name = 'Max Particle Size';
- atmosphereFolder.addSlider(atmosphereParams, 'density', -2, 2, 0.1)..name = 'Density';
- atmosphereFolder.addSlider(atmosphereParams, 'opacity', 0, 1, 0.1)..name = 'Opacity';
- atmosphereFolder.addSlider(atmosphereParams, 'scale', 1, 30)..name = 'Scale';
- atmosphereFolder.addSlider(atmosphereParams, 'speed', 0, 0.1, 0.001)..name = 'Speed';
-
- final atmosphereColorFolder = gui.addFolder('Color')..open();
- atmosphereColorFolder.addColor(atmosphereParams, 'color')..name = 'Color'..onChange((value){
- planet.atmosphere.material?.uniforms['color']['value'] = three.Color.fromHex32(value);
- atmosphere.update();
- });
+ atmosphere?.update(0.0);
+ });
+ atmosphereFolder.addSlider(atmosphereParams!, 'thickness', 0.1, 5, 0.1)..name = 'Thickness';
+ atmosphereFolder.addSlider(atmosphereParams, 'particles', 1, 50000, 1)..name = 'Particle Count';
+ atmosphereFolder.addSlider(atmosphereParams, 'minParticleSize', 0, 200)..name = 'Min Particle Size';
+ atmosphereFolder.addSlider(atmosphereParams, 'maxParticleSize', 0, 200)..name = 'Max Particle Size';
+ atmosphereFolder.addSlider(atmosphereParams, 'density', -2, 2, 0.1)..name = 'Density';
+ atmosphereFolder.addSlider(atmosphereParams, 'opacity', 0, 1, 0.1)..name = 'Opacity';
+ atmosphereFolder.addSlider(atmosphereParams, 'scale', 1, 30)..name = 'Scale';
+ atmosphereFolder.addSlider(atmosphereParams, 'speed', 0, 0.1, 0.001)..name = 'Speed';
+
+ final atmosphereColorFolder = gui.addFolder('Color')..open();
+ atmosphereColorFolder.addColor(atmosphereParams, 'color')..name = 'Color'..onChange((value){
+ planet.atmosphere?.material?.uniforms['color']['value'] = three.Color.fromHex32(value);
+ atmosphere?.update(0.0);
+ });
+ }
final lightingFolder = gui.addFolder('Lighting')..open();
lightingFolder.onChange((name,value){
@@ -256,12 +248,7 @@ class _State extends State {
planet.material?.uniforms[name]['value'] = value;
}
});
- bumpMapFolder.addSlider(planetParams, 'bumpStrength', 0, 1,0.1)..name = 'Bump Strength';
- bumpMapFolder.addSlider(planetParams, 'bumpOffset', 0.0001, 0.1,0.0001)..name = 'Bump Offset';
-
- // final bloomFolder = gui.addFolder('Bloom');
- // bloomFolder.addSlider(bloomPass, 'threshold', 0, 1,0.1);
- // bloomFolder.addSlider(bloomPass, 'strength', 0, 1,0.1);
- // bloomFolder.addSlider(bloomPass, 'radius', 0, 2,0.1);
+ bumpMapFolder.addSlider(planetParams, 'bumpStrength', 0, 2,0.1)..name = 'Bump Strength';
+ bumpMapFolder.addSlider(planetParams, 'bumpOffset', 0.0, 0.1,0.0001)..name = 'Bump Offset';
}
}
diff --git a/examples/lib/texture/webgl_materials_video_webcam.dart b/examples/lib/texture/webgl_materials_video_webcam.dart
index 9efc8467..a84d7acc 100644
--- a/examples/lib/texture/webgl_materials_video_webcam.dart
+++ b/examples/lib/texture/webgl_materials_video_webcam.dart
@@ -24,7 +24,7 @@ class _State extends State {
bool loading = true;
three.ThreeJS? threeJs;
three.CanvasTexture? texture;
- late three.Uint8Array image;
+ late Uint8List image;
Size imageSize = const Size(640,480);
@override
@@ -37,7 +37,7 @@ class _State extends State {
await camera.startLiveFeed((InputImage i){
if(threeJs == null){
imageSize = i.metadata!.size;
- image = three.Uint8Array((imageSize.width*imageSize.height*4).toInt());
+ image = Uint8List((imageSize.width*imageSize.height*4).toInt());
threeJs = three.ThreeJS(
onSetupComplete: (){setState(() {});},
setup: setup,
diff --git a/examples/lib/texture/webgl_opengl_texture.dart b/examples/lib/texture/webgl_opengl_texture.dart
index fa4e744f..5cdc38fa 100644
--- a/examples/lib/texture/webgl_opengl_texture.dart
+++ b/examples/lib/texture/webgl_opengl_texture.dart
@@ -1,4 +1,5 @@
import 'dart:async';
+import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:example/src/statistics.dart';
import 'package:three_js/three_js.dart' as three;
@@ -82,11 +83,11 @@ class _State extends State {
final cubes = []; // just an array we can use to rotate the cubes
{
- final three.RenderingContext gl = threeJs.renderer!.getContext();
+ final RenderingContext gl = threeJs.renderer!.getContext();
final glTex = gl.createTexture();
gl.bindTexture(WebGL.TEXTURE_2D, glTex);
gl.texImage2D(WebGL.TEXTURE_2D, 0, WebGL.RGBA, 2, 2, 0,
- WebGL.RGBA, WebGL.UNSIGNED_BYTE, Uint8Array.fromList([
+ WebGL.RGBA, WebGL.UNSIGNED_BYTE, Uint8List.fromList([
255, 0, 0, 255,
0, 255, 0, 255,
0, 0, 255, 255,
diff --git a/examples/lib/volume/webgl_volume_cloud.dart b/examples/lib/volume/webgl_volume_cloud.dart
index 816f605d..b495feeb 100644
--- a/examples/lib/volume/webgl_volume_cloud.dart
+++ b/examples/lib/volume/webgl_volume_cloud.dart
@@ -1,4 +1,5 @@
import 'dart:async';
+import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:example/src/statistics.dart';
import 'package:three_js/three_js.dart' as three;
@@ -62,7 +63,7 @@ class _State extends State {
controls = three.OrbitControls( threeJs.camera, threeJs.globalKey );
const size = 128;
- final data = three.Uint8Array( size * size * size );
+ final data = Uint8List( size * size * size );
int i = 0;
const scale = 0.05;
diff --git a/examples/lib/volume/webgl_volume_instancing.dart b/examples/lib/volume/webgl_volume_instancing.dart
index 2686eca6..6292d0ae 100644
--- a/examples/lib/volume/webgl_volume_instancing.dart
+++ b/examples/lib/volume/webgl_volume_instancing.dart
@@ -203,7 +203,7 @@ class _State extends State {
three.Material? material,
three.Object3D? mesh,
three.RenderTarget? renderTarget,
- three.WebGLRenderer? renderer,
+ three.Renderer? renderer,
three. Scene? scene
}) {
material?.uniforms['cameraPos']['value'].setFrom( threeJs.camera.position );
diff --git a/examples/lib/volume/webgl_volume_perlin.dart b/examples/lib/volume/webgl_volume_perlin.dart
index 771c1492..30bf2c2d 100644
--- a/examples/lib/volume/webgl_volume_perlin.dart
+++ b/examples/lib/volume/webgl_volume_perlin.dart
@@ -1,4 +1,5 @@
import 'dart:async';
+import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:example/src/statistics.dart';
import 'package:three_js/three_js.dart' as three;
@@ -69,7 +70,7 @@ class _State extends State {
// Texture
const size = 128;
- final data = three.Uint8Array( size * size * size );
+ final data = Uint8List( size * size * size );
int i = 0;
final perlin = ImprovedNoise();
diff --git a/examples/pubspec.yaml b/examples/pubspec.yaml
index dc90ba74..71d6e7f0 100644
--- a/examples/pubspec.yaml
+++ b/examples/pubspec.yaml
@@ -4,7 +4,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 1.0.0+1
environment:
- sdk: '>=3.3.3 <4.0.0'
+ sdk: '>=3.8.1 <4.0.0'
dependencies:
flutter:
@@ -17,8 +17,8 @@ dependencies:
css:
git:
url: https://github.com/Knightro63/css.git
- three_js: ^0.2.7
- #path: ../packages/three_js
+ three_js:
+ path: ../packages/three_js
three_js_helpers: ^0.2.3
three_js_transform_controls: ^0.2.1
three_js_svg: ^0.2.1
@@ -54,7 +54,63 @@ dependency_overrides:
file_picker: ^10.1.9
three_js_objects:
path: ../packages/three_js_objects
-
+ three_js_math:
+ path: ../packages/three_js_math
+ three_js_core:
+ path: ../packages/three_js_core
+ flutter_angle:
+ path: ../../flutter_angle/flutter_angle
+ three_js_animations:
+ path: ../packages/three_js_animations
+ three_js_audio:
+ path: ../packages/three_js_audio
+ three_js_audio_latency:
+ path: ../packages/three_js_audio_latency
+ three_js_exporters:
+ path: ../packages/three_js_exporters
+ three_js_helpers:
+ path: ../packages/three_js_helpers
+ three_js_line:
+ path: ../packages/three_js_line
+ three_js_modifers:
+ path: ../packages/three_js_modifers
+ three_js_postprocessing:
+ path: ../packages/three_js_postprocessing
+ three_js_svg:
+ path: ../packages/three_js_svg
+ three_js_tjs_loader:
+ path: ../packages/three_js_tjs_loader
+ three_js_terrain:
+ path: ../packages/three_js_terrain
+ three_js_video_texture:
+ path: ../packages/three_js_video_texture
+ three_js_advanced_exporters:
+ path: ../packages/three_js_advanced_exporters
+ three_js_advanced_loaders:
+ path: ../packages/three_js_advanced_loaders
+ three_js_bvh_csg:
+ path: ../packages/three_js_bvh_csg
+ three_js_transform_controls:
+ path: ../packages/three_js_transform_controls
+ three_js_controls:
+ path: ../packages/three_js_controls
+ three_js_core_loaders:
+ path: ../packages/three_js_core_loaders
+ three_js_curves:
+ path: ../packages/three_js_curves
+ three_js_simple_loaders:
+ path: ../packages/three_js_simple_loaders
+ three_js_text:
+ path: ../packages/three_js_text
+ three_js_geometry:
+ path: ../packages/three_js_geometry
+ three_js_sensors:
+ path: ../packages/three_js_sensors
+ three_js_angle_renderer:
+ path: ../packages/three_js_angle_renderer
+ # three_js_webgl_renderer:
+ # path: ../packages/three_js_webgl_renderer
+
flutter:
uses-material-design: true
assets:
diff --git a/packages/three_js/CHANGELOG.md b/packages/three_js/CHANGELOG.md
index d26038fc..4d1f2446 100755
--- a/packages/three_js/CHANGELOG.md
+++ b/packages/three_js/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 0.3.0
+
+* Removed NativeArray for Zero-Allocation Updates.
+* Added: three_js_angle_renderer
+
## 0.2.8
* Updated to the latest version of three_js apis
diff --git a/packages/three_js/README.md b/packages/three_js/README.md
index 6e58832a..0ec1838e 100755
--- a/packages/three_js/README.md
+++ b/packages/three_js/README.md
@@ -57,6 +57,7 @@ This is a dart conversion of three.js and three_dart, originally created by [@mr
- WebGL2 supported. please add `` to your index.html to load the js_interop file.
**Linux**
+ - Currenlty only works for flutter < 3.27
- Ubuntu supported (Tested on Linux Mint)
- OpenGL supported
@@ -69,6 +70,12 @@ Please use [Permission Handler](https://pub.dev/packages/permission_handler) pac
This project is a simple 3D rendering engine for flutter to view, edit, or manipulate 3D models.
+## Legacy
+
+Please use [three_js_webgl](https://pub.dev/packages/three_js_webgl) for three_dart type renderer.
+
+As this project moves twards impeller renderer google's ANGLE will be removed and this version will be moved to three_js_angle, which currenly does not exists.
+
## Example
Find the example for this API [here](https://github.com/Knightro63/three_js/tree/main/packages/three_js/example/), for more examples you can click [here](https://github.com/Knightro63/three_js/tree/main/examples/), and for a preview go [here](https://knightro63.github.io/three_js/).
diff --git a/packages/three_js/lib/three_js.dart b/packages/three_js/lib/three_js.dart
index b59b7fe0..9bd5db91 100755
--- a/packages/three_js/lib/three_js.dart
+++ b/packages/three_js/lib/three_js.dart
@@ -10,4 +10,6 @@ export 'package:three_js_core_loaders/three_js_core_loaders.dart';
export 'package:three_js_curves/three_js_curves.dart';
export 'package:three_js_simple_loaders/three_js_simple_loaders.dart';
export 'package:three_js_text/three_js_text.dart';
-export 'package:three_js_geometry/three_js_geometry.dart';
\ No newline at end of file
+export 'package:three_js_geometry/three_js_geometry.dart';
+export 'package:three_js_angle_renderer/three_js_angle_renderer.dart';
+//export 'package:three_js_webgl_renderer/three_js_webgl_renderer.dart';
\ No newline at end of file
diff --git a/packages/three_js/pubspec.yaml b/packages/three_js/pubspec.yaml
index b53753d5..b3f4baeb 100755
--- a/packages/three_js/pubspec.yaml
+++ b/packages/three_js/pubspec.yaml
@@ -1,6 +1,6 @@
name: three_js
description: "Flutter package converted from threejs and three_dart to allow users to view, edit, and control 3D models."
-version: 0.2.8
+version: 0.3.0
homepage: https://github.com/Knightro63/three_js/tree/main/packages/three_js
topics:
@@ -17,16 +17,18 @@ environment:
dependencies:
flutter:
sdk: flutter
- three_js_math: ^0.2.6
- three_js_advanced_loaders: ^0.2.5
- three_js_animations: ^0.2.3
- three_js_controls: ^0.2.2
- three_js_core: ^0.2.8
- three_js_core_loaders: ^0.2.2
- three_js_curves: ^0.2.1
- three_js_simple_loaders: ^0.2.2
- three_js_text: ^0.2.0
- three_js_geometry: ^0.2.1
+ three_js_angle_renderer: ^0.0.1
+ #three_js_webgl_renderer: ^0.0.1
+ three_js_math: ^0.3.0
+ three_js_advanced_loaders: ^0.3.0
+ three_js_animations: ^0.3.0
+ three_js_controls: ^0.3.0
+ three_js_core: ^0.3.0
+ three_js_core_loaders: ^0.3.0
+ three_js_curves: ^0.3.0
+ three_js_simple_loaders: ^0.3.0
+ three_js_text: ^0.3.0
+ three_js_geometry: ^0.3.0
dev_dependencies:
flutter_test:
diff --git a/packages/three_js_advanced_exporters/CHANGELOG.md b/packages/three_js_advanced_exporters/CHANGELOG.md
index 9b7b03ba..0858c59d 100755
--- a/packages/three_js_advanced_exporters/CHANGELOG.md
+++ b/packages/three_js_advanced_exporters/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.1.0
+
+* Removed NativeArray for Zero-Allocation Updates.
+
## 0.0.1
* Initial release.
\ No newline at end of file
diff --git a/packages/three_js_advanced_exporters/lib/usdz/image_export_app.dart b/packages/three_js_advanced_exporters/lib/usdz/image_export_app.dart
index dbc10265..c1e17d58 100644
--- a/packages/three_js_advanced_exporters/lib/usdz/image_export_app.dart
+++ b/packages/three_js_advanced_exporters/lib/usdz/image_export_app.dart
@@ -1,11 +1,10 @@
import 'dart:typed_data';
import 'package:image/image.dart' hide Color;
import 'package:three_js_core/three_js_core.dart';
-import 'package:three_js_math/three_js_math.dart';
class ImageExport{
static Future decodeImageFromList(ImageElement element, bool flipY, int maxTextureSize) async {
- ByteBuffer bytes = Uint8List.fromList((element.data as Uint8Array).toDartList()).buffer;
+ final ByteBuffer bytes = element.data.buffer;
Image image = Image.fromBytes(
width: element.width.toInt(),
diff --git a/packages/three_js_advanced_exporters/lib/usdz/image_export_web.dart b/packages/three_js_advanced_exporters/lib/usdz/image_export_web.dart
index 74c4e7ac..05e31e6a 100644
--- a/packages/three_js_advanced_exporters/lib/usdz/image_export_web.dart
+++ b/packages/three_js_advanced_exporters/lib/usdz/image_export_web.dart
@@ -5,12 +5,11 @@ import 'dart:math' as math;
import 'package:web/web.dart';
import 'package:image/image.dart' hide Color;
import 'package:three_js_core/three_js_core.dart' as core;
-import 'package:three_js_math/three_js_math.dart';
class ImageExport{
static Future decodeImageFromList(core.ImageElement element, bool flipY, int maxTextureSize) async {
- if(element.data is Uint8Array){
- ByteBuffer bytes = Uint8List.fromList((element.data as Uint8Array).toDartList()).buffer;
+ if(element.data is Uint8List){
+ final ByteBuffer bytes = element.data.buffer;
Image image = Image.fromBytes(
width: element.width.toInt(),
height: element.height.toInt(),
diff --git a/packages/three_js_advanced_exporters/pubspec.yaml b/packages/three_js_advanced_exporters/pubspec.yaml
index 12aaef82..01090332 100755
--- a/packages/three_js_advanced_exporters/pubspec.yaml
+++ b/packages/three_js_advanced_exporters/pubspec.yaml
@@ -1,6 +1,6 @@
name: three_js_advanced_exporters
description: "Flutter three_js_advanced_exporters package exports usdz from three_js."
-version: 0.0.1
+version: 0.1.0
homepage: https://github.com/Knightro63/three_js/tree/main/packages/three_js_advanced_exporters
environment:
@@ -14,9 +14,9 @@ dependencies:
path_provider: ^2.1.5
image: ^4.5.4
web: ^1.1.1
- three_js_exporters: ^0.2.0
- three_js_core: ^0.2.0
- three_js_math: ^0.2.0
+ three_js_exporters: ^0.3.0
+ three_js_core: ^0.3.0
+ three_js_math: ^0.3.0
dev_dependencies:
flutter_test:
diff --git a/packages/three_js_advanced_loaders/CHANGELOG.md b/packages/three_js_advanced_loaders/CHANGELOG.md
index 14afeb82..b2cac606 100755
--- a/packages/three_js_advanced_loaders/CHANGELOG.md
+++ b/packages/three_js_advanced_loaders/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.3.0
+
+* Removed NativeArray for Zero-Allocation Updates.
+
## 0.2.5
* Added PCD Loader
diff --git a/packages/three_js_advanced_loaders/lib/dds_loader.dart b/packages/three_js_advanced_loaders/lib/dds_loader.dart
index cb74977b..88a5fd61 100644
--- a/packages/three_js_advanced_loaders/lib/dds_loader.dart
+++ b/packages/three_js_advanced_loaders/lib/dds_loader.dart
@@ -157,10 +157,10 @@ class DDSLoader extends CompressedTextureLoader {
]);
}
- Uint8Array loadARGBMip(ByteBuffer buffer, int dataOffset, int width, int height ) {
+ Uint8List loadARGBMip(ByteBuffer buffer, int dataOffset, int width, int height ) {
final dataLength = width * height * 4;
- final srcBuffer = new Uint8Array.fromList( buffer.asUint8List(dataOffset, dataLength));
- final byteArray = new Uint8Array( dataLength );
+ final srcBuffer = Uint8List.fromList( buffer.asUint8List(dataOffset, dataLength));
+ final byteArray = Uint8List( dataLength );
int dst = 0;
int src = 0;
@@ -177,14 +177,14 @@ class DDSLoader extends CompressedTextureLoader {
}
}
- srcBuffer.dispose();
+ //srcBuffer.dispose();
return byteArray;
}
- Uint8Array loadRGBMip(ByteBuffer buffer, int dataOffset, int width, int height ) {
+ Uint8List loadRGBMip(ByteBuffer buffer, int dataOffset, int width, int height ) {
final dataLength = width * height * 3;
- final srcBuffer = new Uint8Array.fromList(buffer.asUint8List(dataOffset, dataLength));
- final byteArray = new Uint8Array( width * height * 4 );
+ final srcBuffer = Uint8List.fromList(buffer.asUint8List(dataOffset, dataLength));
+ final byteArray = Uint8List( width * height * 4 );
int dst = 0;
int src = 0;
@@ -241,7 +241,7 @@ class DDSLoader extends CompressedTextureLoader {
// Parse header
- final header = new Int32Array.fromList(buffer.asInt32List(0, headerLengthInt));
+ final header = new Int32List.fromList(buffer.asInt32List(0, headerLengthInt));
if ( header[ off_magic ] != DDS_MAGIC ) {
console.error( 'THREE.DDSLoader.parse: Invalid magic number in DDS header.' );
@@ -275,7 +275,7 @@ class DDSLoader extends CompressedTextureLoader {
}
else if(FOURCC_DX10 == fourCC){
dataOffset += extendedHeaderLengthInt * 4;
- final extendedHeader = new Int32Array.fromList( buffer.asInt32List(( headerLengthInt + 1 ) * 4, extendedHeaderLengthInt));
+ final extendedHeader = new Int32List.fromList( buffer.asInt32List(( headerLengthInt + 1 ) * 4, extendedHeaderLengthInt));
final dxgiFormat = extendedHeader[ off_dxgiFormat ];
if( DXGI_FORMAT_BC6H_SF16 == dxgiFormat){
@@ -351,7 +351,7 @@ class DDSLoader extends CompressedTextureLoader {
int height = dds.height;
for (int i = 0; i < dds.mipmapCount; i ++ ) {
- NativeArray byteArray;
+ TypedDataList byteArray;
int dataLength;
if ( isRGBAUncompressed ) {
@@ -364,7 +364,7 @@ class DDSLoader extends CompressedTextureLoader {
}
else {
dataLength = (math.max( 4, width ) / 4 * math.max( 4, height ) / 4 * blockBytes).toInt();
- byteArray = new Uint8Array.fromList( buffer.asUint8List(dataOffset, dataLength));
+ byteArray = Uint8List.fromList( buffer.asUint8List(dataOffset, dataLength));
}
final mipmap = { 'data': byteArray, 'width': width, 'height': height };
diff --git a/packages/three_js_advanced_loaders/lib/gltf/gltf_helper.dart b/packages/three_js_advanced_loaders/lib/gltf/gltf_helper.dart
index 1edadc87..8a349ad5 100755
--- a/packages/three_js_advanced_loaders/lib/gltf/gltf_helper.dart
+++ b/packages/three_js_advanced_loaders/lib/gltf/gltf_helper.dart
@@ -1,7 +1,6 @@
import 'dart:async';
import 'dart:typed_data';
import 'dart:math' as math;
-import 'package:flutter/foundation.dart';
import 'gltf_registry.dart';
import 'gltf_parser.dart';
@@ -102,18 +101,18 @@ class GLTypeData {
} else if (array is Float32List) {
return Float32BufferAttribute.fromList(array, itemSize, normalized);
} else {
- throw ("GLTFHelper createBufferAttribute array.runtimeType : ${array.runtimeType} is not support yet");
+ throw ("GLTFHelper createBufferAttribute array: ${array} is not support yet");
}
}
}
-final MapwebglComponentTypes = {
- 5120: (int s){return Int8Array(s);},
- 5121: (int s){return Uint8Array(s);},
- 5122: (int s){return Int16Array(s);},
- 5123: (int s){return Uint16Array(s);},
- 5125: (int s){return Uint32Array(s);},
- 5126: (int s){return Float32Array(s);}
+final MapwebglComponentTypes = {
+ 5120: (int s){return Int8List(s);},
+ 5121: (int s){return Uint8List(s);},
+ 5122: (int s){return Int16List(s);},
+ 5123: (int s){return Uint16List(s);},
+ 5125: (int s){return Uint32List(s);},
+ 5126: (int s){return Float32List(s);}
};
final webglCTBPE = {
diff --git a/packages/three_js_advanced_loaders/lib/gltf/gltf_parser.dart b/packages/three_js_advanced_loaders/lib/gltf/gltf_parser.dart
index dfbe3d47..5cb4fcec 100755
--- a/packages/three_js_advanced_loaders/lib/gltf/gltf_parser.dart
+++ b/packages/three_js_advanced_loaders/lib/gltf/gltf_parser.dart
@@ -1,5 +1,6 @@
import 'dart:async';
import 'dart:typed_data';
+import 'dart:math' as math;
import 'package:flutter/foundation.dart';
import 'package:three_js_advanced_loaders/gltf/gltf_extensions.dart';
@@ -466,13 +467,25 @@ class GLTFParser {
final int stride = byteStride ~/ elementBytes;
int totalLen = array.length;
if(array is Uint8List){
- ib = InterleavedBuffer(Uint8Array(totalLen).set(array.buffer.asUint8List()), 1);
+ ib = InterleavedBuffer(Uint8List(totalLen)..set(array.buffer.asUint8List()), 1);
}
else if(array is Int8List){
- ib = InterleavedBuffer(Int8Array(totalLen).set(array.buffer.asInt8List()), 1);
+ ib = InterleavedBuffer(Int8List(totalLen)..set(array.buffer.asInt8List()), 1);
+ }
+ else if(array is Int16List){
+ ib = InterleavedBuffer(Int16List(totalLen)..set(array.buffer.asInt16List()), 1);
+ }
+ else if(array is Int32List){
+ ib = InterleavedBuffer(Int32List(totalLen)..set(array.buffer.asInt32List()), 1);
+ }
+ else if(array is Uint16List){
+ ib = InterleavedBuffer(Uint16List(totalLen)..set(array.buffer.asUint16List()), 1);
+ }
+ else if(array is Uint32List){
+ ib = InterleavedBuffer(Uint32List(totalLen)..set(array.buffer.asUint32List()), 1);
}
else{
- ib = InterleavedBuffer(Float32Array(totalLen).set(array.buffer.asFloat32List()), stride);
+ ib = InterleavedBuffer(Float32List(totalLen)..set(array.buffer.asFloat32List()), stride);
}
parser.cache.add(ibCacheKey, ib);
@@ -928,7 +941,7 @@ class GLTFParser {
} else if (materialType == MeshStandardMaterial) {
return MeshStandardMaterial.fromMap(materialParams);
} else {
- throw ("GLTFParser createMaterialType materialType: ${materialType.runtimeType.toString()} is not support ");
+ throw ("GLTFParser createMaterialType materialType: $materialType is not support ");
}
}
@@ -1298,7 +1311,7 @@ class GLTFParser {
targetNames.add(targetName);
}
- dynamic outputArray = outputAccessor.array.toDartList();
+ dynamic outputArray = outputAccessor.array;
if (outputAccessor.normalized) {
final scale = getNormalizedComponentScale(outputArray.runtimeType);
@@ -1315,7 +1328,7 @@ class GLTFParser {
for (int j = 0, jl = targetNames.length; j < jl; j++) {
final track = typedKeyframeTrack.createTrack(
targetNames[j] + '.' + PathProperties.getValue(target["path"]),
- inputAccessor.array.toDartList(),
+ inputAccessor.array,
outputArray,
interpolation);
diff --git a/packages/three_js_advanced_loaders/lib/ktx_loader.dart b/packages/three_js_advanced_loaders/lib/ktx_loader.dart
index 561beb57..bb69176f 100644
--- a/packages/three_js_advanced_loaders/lib/ktx_loader.dart
+++ b/packages/three_js_advanced_loaders/lib/ktx_loader.dart
@@ -145,11 +145,11 @@ class KhronosTextureContainer {
final mipmapCount = loadMipmaps ? numberOfMipmapLevels : 1;
for ( int level = 0; level < mipmapCount; level ++ ) {
- final imageSize = arrayBuffer.asInt32List(dataOffset, 1)[0];// Int32Array(arrayBuffer, )[ 0 ]; // size per face, since not supporting array cubemaps
+ final imageSize = arrayBuffer.asInt32List(dataOffset, 1)[0];// Int32List(arrayBuffer, )[ 0 ]; // size per face, since not supporting array cubemaps
dataOffset += 4; // size of the image + 4 for the imageSize field
for ( int face = 0; face < numberOfFaces; face ++ ) {
- final byteArray = arrayBuffer.asUint8List().sublist(dataOffset, imageSize);//Uint8Array(arrayBuffer, dataOffset, imageSize );
+ final byteArray = arrayBuffer.asUint8List().sublist(dataOffset, imageSize);//Uint8List(arrayBuffer, dataOffset, imageSize );
mipmaps.add( { 'data': byteArray, 'width': width, 'height': height } );
dataOffset += imageSize;
diff --git a/packages/three_js_advanced_loaders/lib/pcd_loader.dart b/packages/three_js_advanced_loaders/lib/pcd_loader.dart
index ac95db1d..b342294a 100644
--- a/packages/three_js_advanced_loaders/lib/pcd_loader.dart
+++ b/packages/three_js_advanced_loaders/lib/pcd_loader.dart
@@ -406,9 +406,9 @@ class PCDLoader extends Loader {
if ( rgb_type == 'F' ) {
// treat float values as int
// https://github.com/daavoo/pyntcloud/pull/204/commits/7b4205e64d5ed09abe708b2e91b615690c24d518
- final farr = new Float32Array( 1 );
+ final farr = new Float32List( 1 );
farr[ 0 ] = float;
- rgb = new Int32Array.fromList( farr.toDartList().buffer.asInt32List() )[ 0 ];
+ rgb = new Int32List.fromList( farr.buffer.asInt32List() )[ 0 ];
}
final r = ( ( rgb >> 16 ) & 0x0000ff ) / 255;
diff --git a/packages/three_js_advanced_loaders/lib/rgbe_loader.dart b/packages/three_js_advanced_loaders/lib/rgbe_loader.dart
index a3d22ead..11e9f96e 100755
--- a/packages/three_js_advanced_loaders/lib/rgbe_loader.dart
+++ b/packages/three_js_advanced_loaders/lib/rgbe_loader.dart
@@ -401,7 +401,7 @@ class RGBELoader extends DataTextureLoader {
destArray[destOffset + 3] = MathUtils.toHalfFloat(1.0);
}
- // final byteArray = Uint8Array( buffer );
+ // final byteArray = Uint8List.fromList( buffer );
// byteArray.pos = 0;
final byteArray = buffer;
@@ -431,7 +431,7 @@ class RGBELoader extends DataTextureLoader {
case FloatType:
numElements = imageRgbaData.length ~/ 4;
- final floatArray = Float32Array(numElements * 4);
+ final floatArray = Float32List(numElements * 4);
for (int j = 0; j < numElements; j++) {
rgbeByteToRGBFloat(imageRgbaData, j * 4, floatArray, j * 4);
}
@@ -442,7 +442,7 @@ class RGBELoader extends DataTextureLoader {
case HalfFloatType:
numElements = imageRgbaData.length ~/ 4;
- final halfArray = Uint16Array(numElements * 4);
+ final halfArray = Uint16List(numElements * 4);
for (int j = 0; j < numElements; j++) {
rgbeByteToRGBHalf(imageRgbaData, j * 4, halfArray, j * 4);
}
diff --git a/packages/three_js_advanced_loaders/lib/tga_loader.dart b/packages/three_js_advanced_loaders/lib/tga_loader.dart
index a40b52a7..e26f4269 100644
--- a/packages/three_js_advanced_loaders/lib/tga_loader.dart
+++ b/packages/three_js_advanced_loaders/lib/tga_loader.dart
@@ -477,7 +477,7 @@ class TGALoader extends DataTextureLoader {
getTgaRGBA( imageData, header['width'] as int, header['height'] as int, result['pixel_data'], result['palettes'] );
final dt = DataTexture(
- Uint8Array.fromList(imageData),
+ Uint8List.fromList(imageData),
header['width'] as int,
header['height'] as int,
);
diff --git a/packages/three_js_advanced_loaders/lib/usdz/usdz_loader.dart b/packages/three_js_advanced_loaders/lib/usdz/usdz_loader.dart
index f74d4b63..e09dd246 100644
--- a/packages/three_js_advanced_loaders/lib/usdz/usdz_loader.dart
+++ b/packages/three_js_advanced_loaders/lib/usdz/usdz_loader.dart
@@ -273,9 +273,9 @@ class USDZLoader extends Loader {
}
Float32BufferAttribute toFlatBufferAttribute(Float32BufferAttribute attribute, List indices ) {
- final Float32Array array = attribute.array;
+ final Float32List array = attribute.array;
final itemSize = attribute.itemSize;
- late final Float32Array array2 = Float32Array(indices.length * itemSize);//array.constructor( indices.length * itemSize );
+ late final Float32List array2 = Float32List(indices.length * itemSize);//array.constructor( indices.length * itemSize );
int index = 0, index2 = 0;
for ( int i = 0, l = indices.length; i < l; i ++ ) {
diff --git a/packages/three_js_advanced_loaders/pubspec.yaml b/packages/three_js_advanced_loaders/pubspec.yaml
index 3ed613a8..2387812f 100755
--- a/packages/three_js_advanced_loaders/pubspec.yaml
+++ b/packages/three_js_advanced_loaders/pubspec.yaml
@@ -1,6 +1,6 @@
name: three_js_advanced_loaders
description: "Flutter advanced loaders package e.g.(gltf, glb and fbx) converted from threejs and three_dart package."
-version: 0.2.5
+version: 0.3.0
homepage: https://github.com/Knightro63/three_js/tree/main/packages/three_js_advanced_loaders
topics:
@@ -18,13 +18,13 @@ dependencies:
xml: ^6.5.0
flutter:
sdk: flutter
- three_js_core: ^0.2.0
- three_js_core_loaders: ^0.2.1
- three_js_curves: ^0.2.0
- three_js_math: ^0.2.0
- three_js_geometry: ^0.2.0
- three_js_animations: ^0.2.1
- three_js_controls: ^0.2.0
+ three_js_core: ^0.3.0
+ three_js_core_loaders: ^0.3.0
+ three_js_curves: ^0.3.0
+ three_js_math: ^0.3.0
+ three_js_geometry: ^0.3.0
+ three_js_animations: ^0.3.0
+ three_js_controls: ^0.3.0
archive: ^4.0.7
dev_dependencies:
diff --git a/packages/three_js_angle/.gitignore b/packages/three_js_angle/.gitignore
new file mode 100755
index 00000000..2e198e1d
--- /dev/null
+++ b/packages/three_js_angle/.gitignore
@@ -0,0 +1,141 @@
+# Miscellaneous
+*.class
+*.lock
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+.metadata
+
+# Visual Studio Code related
+.classpath
+.project
+.settings/
+.vscode/*
+
+*/macos
+*/web
+*/ios
+*/android
+*/linux
+*/windows
+
+# Flutter repo-specific
+/bin/cache/
+/bin/internal/bootstrap.bat
+/bin/internal/bootstrap.sh
+/bin/mingit/
+/dev/benchmarks/mega_gallery/
+/dev/bots/.recipe_deps
+/dev/bots/android_tools/
+/dev/devicelab/ABresults*.json
+/dev/docs/doc/
+/dev/docs/flutter.docs.zip
+/dev/docs/lib/
+/dev/docs/pubspec.yaml
+/dev/integration_tests/**/xcuserdata
+/dev/integration_tests/**/Pods
+/packages/flutter/coverage/
+version
+analysis_benchmark.json
+
+# packages file containing multi-root paths
+.packages.generated
+
+# Flutter/Dart/Pub related
+**/doc/api/
+.dart_tool/
+.flutter-plugins
+.flutter-plugins-dependencies
+**/generated_plugin_registrant.dart
+.packages
+.pub-preload-cache/
+.pub/
+build/
+flutter_*.png
+linked_*.ds
+unlinked.ds
+unlinked_spec.ds
+
+# Android related
+**/android/**/gradle-wrapper.jar
+.gradle/
+**/android/captures/
+**/android/gradlew
+**/android/gradlew.bat
+**/android/local.properties
+**/android/**/GeneratedPluginRegistrant.java
+**/android/key.properties
+*.jks
+
+# iOS/XCode related
+**/ios/**/*.mode1v3
+**/ios/**/*.mode2v3
+**/ios/**/*.moved-aside
+**/ios/**/*.pbxuser
+**/ios/**/*.perspectivev3
+**/ios/**/*sync/
+**/ios/**/.sconsign.dblite
+**/ios/**/.tags*
+**/ios/**/.vagrant/
+**/ios/**/DerivedData/
+**/ios/**/Icon?
+**/ios/**/Pods/
+**/ios/**/.symlinks/
+**/ios/**/profile
+**/ios/**/xcuserdata
+**/ios/.generated/
+**/ios/Flutter/.last_build_id
+**/ios/Flutter/App.framework
+**/ios/Flutter/Flutter.framework
+**/ios/Flutter/Flutter.podspec
+**/ios/Flutter/Generated.xcconfig
+**/ios/Flutter/ephemeral
+**/ios/Flutter/app.flx
+**/ios/Flutter/app.zip
+**/ios/Flutter/flutter_assets/
+**/ios/Flutter/flutter_export_environment.sh
+**/ios/ServiceDefinitions.json
+**/ios/Runner/GeneratedPluginRegistrant.*
+
+# macOS
+**/Flutter/ephemeral/
+**/Pods/
+**/macos/Flutter/GeneratedPluginRegistrant.swift
+**/macos/Flutter/ephemeral
+**/xcuserdata/
+
+# Windows
+**/windows/flutter/generated_plugin_registrant.cc
+**/windows/flutter/generated_plugin_registrant.h
+**/windows/flutter/generated_plugins.cmake
+
+# Linux
+**/linux/flutter/generated_plugin_registrant.cc
+**/linux/flutter/generated_plugin_registrant.h
+**/linux/flutter/generated_plugins.cmake
+
+# Coverage
+coverage/
+
+# Symbols
+app.*.symbols
+
+# Exceptions to above rules.
+!**/ios/**/default.mode1v3
+!**/ios/**/default.mode2v3
+!**/ios/**/default.pbxuser
+!**/ios/**/default.perspectivev3
+!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
+!/dev/ci/**/Gemfile.lock
+!.vscode/settings.json
\ No newline at end of file
diff --git a/packages/three_js_angle/CHANGELOG.md b/packages/three_js_angle/CHANGELOG.md
new file mode 100755
index 00000000..9b7b03ba
--- /dev/null
+++ b/packages/three_js_angle/CHANGELOG.md
@@ -0,0 +1,3 @@
+## 0.0.1
+
+* Initial release.
\ No newline at end of file
diff --git a/packages/three_js_angle/LICENSE b/packages/three_js_angle/LICENSE
new file mode 100755
index 00000000..383b4da5
--- /dev/null
+++ b/packages/three_js_angle/LICENSE
@@ -0,0 +1,21 @@
+The MIT License
+
+Copyright © 2010-2024 three.js authors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/packages/three_js_angle/README.md b/packages/three_js_angle/README.md
new file mode 100755
index 00000000..58590e20
--- /dev/null
+++ b/packages/three_js_angle/README.md
@@ -0,0 +1,469 @@
+# three_js_angle
+
+[](https://pub.dev/packages/three_js_angle)
+[](https://opensource.org/licenses/MIT)
+
+A 3D rendering engine for dart (based on [three.js](https://github.com/mrdoob/three.js) and [three_dart](https://github.com/wasabia/three_dart)) that allows users to view, edit and manipulate their 3D objects. The current builds uses [angle](https://github.com/google/angle) for desktop and mobile, and WebGL2 for web applications.
+
+## Features
+
+
+
+This is a dart conversion of three.js and three_dart, originally created by [@mrdoob](https://github.com/mrdoob) and has a coverted dart fork by [@wasabia](https://github.com/wasabia).
+
+## Requirements
+
+**MacOS**
+ - Minimum os Deployment Target: 10.15
+ - Xcode 13 or newer
+ - Swift 5
+ - Metal supported
+
+**iOS**
+ - Minimum os Deployment Target: 13.0
+ - Xcode 13 or newer
+ - Swift 5
+ - Metal supported
+
+**iOS-Simulator**
+ - Minimum os Deployment Target: 13.0
+ - Xcode 13 or newer
+ - Swift 5
+ - Metal supported
+
+**Android**
+ - compileSdkVersion: 34
+ - minSdk: 21
+ - OpenGL supported
+ - Vulkan supported
+
+**Android Emulator**
+ - compileSdkVersion: 34
+ - minSdk: 21
+ - OpenGL supported
+
+**Windows**
+ - Intel supported
+ - AMD supported
+ - Qualcom supported
+ - Direct3D 11 supported
+ - OpenGL supported
+
+**Web**
+ - WebGL2 supported. please add `` to your index.html to load the js_interop file.
+
+**WASM**
+ - WebGL2 supported. please add `` to your index.html to load the js_interop file.
+
+**Linux**
+ - Currenlty only works for flutter < 3.27
+ - Ubuntu supported (Tested on Linux Mint)
+ - OpenGL supported
+
+## Getting started
+
+To get started add three_js to your pubspec.yaml file. Adding permissions for audio and video is required if using either item.
+Please use [Permission Handler](https://pub.dev/packages/permission_handler) package to help with this.
+
+## Usage
+
+This project is a simple 3D rendering engine for flutter to view, edit, or manipulate 3D models.
+
+## Example
+
+Find the example for this API [here](https://github.com/Knightro63/three_js/tree/main/packages/three_js/example/), for more examples you can click [here](https://github.com/Knightro63/three_js/tree/main/examples/), and for a preview go [here](https://knightro63.github.io/three_js/).
+
+## Know Issues
+
+**All**
+ - MD2 annimations do not work
+ - Collada animations do not work
+ - Collada kinnametics does not work
+ - PMREM gives weird artifacts or is completely black
+
+**MacOS**
+ - N/A
+
+**iOS**
+ - Protoplanets does not function correctly
+
+**Android**
+ - Morphtargets does not work on some devices
+ - Some RGBELoaders cause app to crash
+
+**Windows**
+ - Tonemapping turns screen black
+ - Some RGBELoaders cause app to crash
+
+**Web**
+ - Lens Flare not working correctly
+ - Simplify modifer has weird artifacts
+
+ **WASM**
+ - Simple GI does not work
+ - Simplify modifer has weird artifacts
+
+**Linux**
+ - Tonemapping turns screen black
+ - Postprocessing does not work
+ - Track pad does not zoom out
+ - Some RGBELoaders cause app to crash
+
+## Libraries and Plugins
+
+**Other Libs**
+ - [Advanced Exporters](https://pub.dev/packages/three_js_advanced_exporters) a USDZ exporter to your three_js project.
+ - [Audio](https://pub.dev/packages/three_js_audio) an audio api using flutters audioplayer from pub.dev do not use with any other audio package..
+ - [Audio Latency](https://pub.dev/packages/three_js_audio_latency) an audio api using SoLoud from pub.dev do not use with any other audio package..
+ - [BVH CSG](https://pub.dev/packages/three_js_bvh_csg) a bvh csg api for three_js.
+ - [Exporters](https://pub.dev/packages/three_js_exporters) an api to add STL, OBJ or PLY exporter for three_js.
+ - [Geometry](https://pub.dev/packages/three_js_geometry) an api to add complex geometries to three_js.
+ - [Line](https://pub.dev/packages/three_js_line) an api to add more line types to three_js.
+ - [Helpers](https://pub.dev/packages/three_js_helpers) an api to add helpers to three_js.
+ - [Modifers](https://pub.dev/packages/three_js_modifers) an api to add simplify or subdivision to three_js.
+ - [Post Processing](https://pub.dev/packages/three_js_postprocessing) a post processor to three_js.
+ - [SVG](https://pub.dev/packages/three_js_svg) an api to add a svg importer and exporter to three_js.
+ - [Three JS Loader](https://pub.dev/packages/three_js_tjs_loader) a loader to add three js json files to three_js.
+ - [Transfrom Controls](https://pub.dev/packages/three_js_transform_controls) a transfor controller for 3d objects for three_js.
+ - [Video Texture](https://pub.dev/packages/three_js_video_texture) an api to add videos and audio to three_js do not use with any other audio package.
+
+**ADD-ONS**
+ - [Omio](https://pub.dev/packages/oimo_physics) a physics engine for three_js.
+ - [Cannon](https://pub.dev/packages/cannon_physics) a physics engine for three_js.
+ - [Terrain](https://pub.dev/packages/three_js_terrain) a map generator for three_js.
+ - [XR](https://pub.dev/packages/three_js_xr) a VR/AR/MR sdk for three_js. (web only)
+ - [Yuka](https://pub.dev/packages/yuka) a game ai sdk for three_js.
+
+## Contributing
+
+Contributions are welcome.
+In case of any problems look at [existing issues](https://github.com/Knightro63/three_js/issues), if you cannot find anything related to your problem then open an issue.
+Create an issue before opening a [pull request](https://github.com/Knightro63/three_js/pulls) for non trivial fixes.
+In case of trivial fixes open a [pull request](https://github.com/Knightro63/three_js/pulls) directly.
+
+## Supported Features
+
+All of the current webgl2 core features are supported at this time.
+GPU is currently under development, so it is currently not supported.
+Please review the following table for all the supported Modules.
+
+ - ✅ Currently Supported
+ - ⚠️ Upon request
+ - ❗ Waiting on GPU update
+ - ❌ Not intended
+
+| Module | Plugin | Web | Mobile | Desktop |
+|-----------------------------------------------------------------------------------------------|--------|------------|---------|-----|
+| | **Animation** | | | |
+| AnimationClipCreator | | ⚠️ | ⚠️ | ⚠️ |
+| CCDIKSolver | | ❌ | ❌ | ❌ |
+| | **Controls** | | | |
+| [ArcballControls](https://github.com/Knightro63/three_js/blob/main/packages/three_js_transform_controls/lib/arcball_controls.dart) | [three_js_transform_controls](https://pub.dev/packages/three_js_transform_controls) | ✅ | ✅ | ✅ |
+| [DragControls](https://github.com/Knightro63/three_js/blob/main/packages/three_js_controls/lib/drag_controls.dart) | [three_js_controls](https://pub.dev/packages/three_js_controls) | ✅ | ✅ | ✅ |
+| [FirstPersonControls](https://github.com/Knightro63/three_js/blob/main/packages/three_js_controls/lib/first_person_controls.dart) | [three_js_controls](https://pub.dev/packages/three_js_controls) | ✅ | ✅ | ✅ |
+| [FlyControls](https://github.com/Knightro63/three_js/blob/main/packages/three_js_controls/lib/fly_controls.dart) | [three_js_controls](https://pub.dev/packages/three_js_controls) | ✅ | ✅ | ✅ |
+| [MapControls](https://github.com/Knightro63/three_js/blob/main/packages/three_js_controls/lib/orbit_controls.dart) | [three_js_controls](https://pub.dev/packages/three_js_controls) | ✅ | ✅ | ✅ |
+| [OrbitControls](https://github.com/Knightro63/three_js/blob/main/packages/three_js_controls/lib/orbit_controls.dart) | [three_js_controls](https://pub.dev/packages/three_js_controls) | ✅ | ✅ | ✅ |
+| [PointerLockControls](https://github.com/Knightro63/three_js/blob/main/packages/three_js_controls/lib/pointer_lock_controls.dart) | [three_js_controls](https://pub.dev/packages/three_js_controls) | ✅ | ✅ | ✅ |
+| [TrackballControls](https://github.com/Knightro63/three_js/blob/main/packages/three_js_controls/lib/trackball_controls.dart) | [three_js_controls](https://pub.dev/packages/three_js_controls) | ✅ | ✅ | ✅ |
+| [TransformControls](https://github.com/Knightro63/three_js/blob/main/packages/three_js_transform_controls/lib/transform_controls.dart) | [three_js_transform_controls](https://pub.dev/packages/three_js_transform_controls) | ✅ | ✅ | ✅ |
+| | **CSM** | | | |
+| [CSM](https://github.com/Knightro63/three_js/blob/main/packages/three_js_objects/lib/csm/csm.dart) | [three_js_objects](https://pub.dev/packages/three_js_objects) | ✅ | ✅ | ✅ |
+| [CSMFrustum](https://github.com/Knightro63/three_js/blob/main/packages/three_js_objects/lib/csm/csm_frustum.dart) | [three_js_objects](https://pub.dev/packages/three_js_objects) | ✅ | ✅ | ✅ |
+| [CSMHelper](https://github.com/Knightro63/three_js/blob/main/packages/three_js_objects/lib/csm/csm_helper.dart) | [three_js_objects](https://pub.dev/packages/three_js_objects) | ✅ | ✅ | ✅ |
+| [CSMShader](https://github.com/Knightro63/three_js/blob/main/packages/three_js_objects/lib/csm/csm_shader.dart) | [three_js_objects](https://pub.dev/packages/three_js_objects) | ✅ | ✅ | ✅ |
+| CSMShadowNode | | ❗ | ❗ | ❗ |
+| | **Curves** | | | |
+| [EXTRAS](https://github.com/Knightro63/three_js/blob/main/packages/three_js_curves/lib/curves/extra.dart) | [three_js_curves](https://pub.dev/packages/three_js_curves) | ✅ | ✅ | ✅ |
+| [NURBSCurve](https://github.com/Knightro63/three_js/blob/main/packages/three_js_curves/lib/nurbs/nurbs_curve.dart) | [three_js_curves](https://pub.dev/packages/three_js_curves) | ✅ | ✅ | ✅ |
+| [NURBSSurface](https://github.com/Knightro63/three_js/blob/main/packages/three_js_curves/lib/nurbs/nurbs_surface.dart) | [three_js_curves](https://pub.dev/packages/three_js_curves) | ✅ | ✅ | ✅ |
+| [NURBSUtils](https://github.com/Knightro63/three_js/blob/main/packages/three_js_curves/lib/nurbs/nurbs_utils.dart) | [three_js_curves](https://pub.dev/packages/three_js_curves) | ✅ | ✅ | ✅ |
+| NURBSVolume | | ⚠️ | ⚠️ | ⚠️ |
+| | **Effects** | | | |
+| AnaglyphEffect | | ⚠️ | ⚠️ | ⚠️ |
+| AsciiEffect | | ⚠️ | ⚠️ | ⚠️ |
+| OutlineEffect | | ⚠️ | ⚠️ | ⚠️ |
+| ParallaxBarrierEffect | | ⚠️ | ⚠️ | ⚠️ |
+| [StereoEffect](https://github.com/Knightro63/three_js/blob/main/packages/three_js_postprocessing/lib/post/stero_effect.dart) | [three_js_postprocessing](https://pub.dev/packages/three_js_postprocessing) | ✅ | ✅ | ✅ |
+| | **Enviroments** | | | |
+| [DebugEnvironment](https://github.com/Knightro63/three_js/blob/main/packages/three_js_helpers/lib/enviroments/debug_environment.dart) | [three_js_helpers](https://pub.dev/packages/three_js_helpers) | ✅ | ✅ | ✅ |
+| [RoomEnvironment](https://github.com/Knightro63/three_js/blob/main/packages/three_js_helpers/lib/enviroments/room_environment.dart) | [three_js_helpers](https://pub.dev/packages/three_js_helpers) | ✅ | ✅ | ✅ |
+| | **Exporters** | | | |
+| DRACOExporter | | ❌ | ❌ | ❌ |
+| EXRExporter | | ❌ | ❌ | ❌ |
+| GLTFExporter | | ⚠️ | ⚠️ | ⚠️ |
+| KTX2Exporter | | ❌ | ❌ | ❌ |
+| [OBJExporter](https://github.com/Knightro63/three_js/blob/main/packages/three_js_exporters/lib/obj_exporter.dart) | [three_js_simple_exporters](https://pub.dev/packages/three_js_exporters) | ✅ | ✅ | ✅ |
+| [PLYExporter](https://github.com/Knightro63/three_js/blob/main/packages/three_js_exporters/lib/ply_exporter.dart) | [three_js_simple_exporters](https://pub.dev/packages/three_js_exporters) | ✅ | ✅ | ✅ |
+| [STLExporter](https://github.com/Knightro63/three_js/blob/main/packages/three_js_exporters/lib/stl_exporter.dart) | [three_js_simple_exporters](https://pub.dev/packages/three_js_exporters) | ✅ | ✅ | ✅ |
+| [USDZExporter](https://github.com/Knightro63/three_js/blob/main/packages/three_js_advanced_exporters/lib/usdz_exporter.dart) | [three_js_advanced_exporters](https://pub.dev/packages/three_js_advanced_exporters) | ✅ | ✅ | ✅ |
+| | **Geometry** | | | |
+| [BoxLineGeometry](https://github.com/Knightro63/three_js/blob/main/packages/three_js_geometry/lib/box_line_geometry.dart) | [three_js_geometry](https://pub.dev/packages/three_js_geometry) | ✅ | ✅ | ✅ |
+| [ConvexGeometry](https://github.com/Knightro63/three_js/blob/main/packages/three_js_geometry/lib/convex.dart) | [three_js_geometry](https://pub.dev/packages/three_js_geometry) | ✅ | ✅ | ✅ |
+| [DecalGeometry](https://github.com/Knightro63/three_js/blob/main/packages/three_js_geometry/lib/decal_geometry.dart) | [three_js_geometry](https://pub.dev/packages/three_js_geometry) | ✅ | ✅ | ✅ |
+| [ParametricFunctions](https://github.com/Knightro63/three_js/blob/main/packages/three_js_geometry/lib/parametric_gemoetries.dart) | [three_js_geometry](https://pub.dev/packages/three_js_geometry) | ✅ | ✅ | ✅ |
+| [ParametricGeometry](https://github.com/Knightro63/three_js/blob/main/packages/three_js_geometry/lib/parametric.dart) | [three_js_geometry](https://pub.dev/packages/three_js_geometry) | ✅ | ✅ | ✅ |
+| RoundedBoxGeometry | | ⚠️ | ⚠️ | ⚠️ |
+| TeapotGeometry | | ⚠️ | ⚠️ | ⚠️ |
+| [TextGeometry](https://github.com/Knightro63/three_js/blob/main/packages/three_js_text/lib/text/text.dart) | [three_js_text](https://pub.dev/packages/three_js_text) | ✅ | ✅ | ✅ |
+| | **Helpers** | | | |
+| [LightProbeHelper](https://github.com/Knightro63/three_js/blob/main/packages/three_js_helpers/lib/light_probe_helper.dart) | [three_js_helpers](https://pub.dev/packages/three_js_helpers) | ✅ | ✅ | ✅ |
+| LightProbeHelperGPU | | ❗ | ❗ | ❗ |
+| [OctreeHelper](https://github.com/Knightro63/three_js/blob/main/packages/three_js_objects/lib/octree_helper.dart) | [three_js_objects](https://pub.dev/packages/three_js_objects) | ✅ | ✅ | ✅ |
+| [PositionalAudioHelper](https://github.com/Knightro63/three_js/blob/main/packages/three_js_core/lib/audio/positional_audio_helper.dart) | [three_js_core](https://pub.dev/packages/three_js_core) | ✅ | ✅ | ✅ |
+| RapierHelper | | ❌ | ❌ | ❌ |
+| [RectAreaLightHelper](https://github.com/Knightro63/three_js/blob/main/packages/three_js_helpers/lib/rect_area_light_helper.dart) | [three_js_helpers](https://pub.dev/packages/three_js_helpers) | ✅ | ✅ | ✅ |
+| TextureHelper | | ⚠️ | ⚠️ | ⚠️ |
+| [VertexNormalsHelper](https://github.com/Knightro63/three_js/blob/main/packages/three_js_helpers/lib/vertex_normals_helper.dart) | [three_js_helpers](https://pub.dev/packages/three_js_helpers) | ✅ | ✅ | ✅ |
+| [VertexTangentsHelper](https://github.com/Knightro63/three_js/blob/main/packages/three_js_helpers/lib/vertex_tangents_helper.dart) | [three_js_helpers](https://pub.dev/packages/three_js_helpers) | ✅ | ✅ | ✅ |
+| [ViewHelper](https://github.com/Knightro63/three_js/blob/main/packages/three_js_helpers/lib/view_helper.dart) | [three_js_helpers](https://pub.dev/packages/three_js_helpers) | ✅ | ✅ | ✅ |
+| | **Interactive** | | | |
+| HTMLMesh | | ❌ | ❌ | ❌ |
+| [InteractiveGroup](https://github.com/Knightro63/three_js/blob/main/packages/three_js_xr/lib/other/interactive_group.dart) | [three_js_xr](https://pub.dev/packages/three_js_xr) | ✅ | ❌ | ❌ |
+| [SelectionBox](https://github.com/Knightro63/three_js/blob/main/packages/three_js_helpers/lib/selection/selection_box.dart) | [three_js_helpers](https://pub.dev/packages/three_js_helpers) | ✅ | ✅ | ✅ |
+| [SelectionHelper](https://github.com/Knightro63/three_js/blob/main/packages/three_js_helpers/lib/selection/selection_helper.dart) | [three_js_helpers](https://pub.dev/packages/three_js_helpers) | ✅ | ✅ | ✅ |
+| | **Lighting** | | | |
+| TiledLighting | | ❗ | ❗ | ❗ |
+| | **Lights** | | | |
+| [LightProbeGenerator](https://github.com/Knightro63/three_js/blob/main/packages/three_js_objects/lib/lights/light_probe_generator.dart) | [three_js_objects](https://pub.dev/packages/three_js_objects) | ✅ | ✅ | ✅ |
+| RectAreaLightTexturesLib | | ⚠️ | ⚠️ | ⚠️ |
+| RectAreaLightUniformsLib | | ⚠️ | ⚠️ | ⚠️ |
+| | **Lines** | | | |
+| [Line2](https://github.com/Knightro63/three_js/blob/main/packages/three_js_line/lib/line2.dart) | [three_js_line](https://pub.dev/packages/three_js_line) | ✅ | ✅ | ✅ |
+| [LineGeometry](https://github.com/Knightro63/three_js/blob/main/packages/three_js_line/lib/line_geometry.dart) | [three_js_line](https://pub.dev/packages/three_js_line) | ✅ | ✅ | ✅ |
+| [LineMaterial](https://github.com/Knightro63/three_js/blob/main/packages/three_js_line/lib/line_material.dart) | [three_js_line](https://pub.dev/packages/three_js_line) | ✅ | ✅ | ✅ |
+| [LineSegments2](https://github.com/Knightro63/three_js/blob/main/packages/three_js_line/lib/line_segments2.dart) | [three_js_line](https://pub.dev/packages/three_js_line) | ✅ | ✅ | ✅ |
+| [LineSegmentsGeometry](https://github.com/Knightro63/three_js/blob/main/packages/three_js_line/lib/line_segments_geometry.dart) | [three_js_line](https://pub.dev/packages/three_js_line) | ✅ | ✅ | ✅ |
+| [Wireframe](https://github.com/Knightro63/three_js/blob/main/packages/three_js_line/lib/wireframe.dart) | [three_js_line](https://pub.dev/packages/three_js_line) | ✅ | ✅ | ✅ |
+| [WireframeGeometry2](https://github.com/Knightro63/three_js/blob/main/packages/three_js_line/lib/wireframe_geometry2.dart) | [three_js_line](https://pub.dev/packages/three_js_line) | ✅ | ✅ | ✅ |
+| | **Loaders** | | | |
+| Rhino3dmLoader | | ❌ | ❌ | ❌ |
+| ThreeMFLoader | | ❌ | ❌ | ❌ |
+| AMFLoader | | ❌ | ❌ | ❌ |
+| [BVHLoader](https://github.com/Knightro63/three_js/blob/main/packages/three_js_advanced_loaders/lib/bvh_loader.dart) | [three_js_advanced_loaders](https://pub.dev/packages/three_js_advanced_loaders) | ✅ | ✅ | ✅ |
+| [ColladaLoader](https://github.com/Knightro63/three_js/blob/main/packages/three_js_advanced_loaders/lib/collada/collada_loader.dart) | [three_js_advanced_loaders](https://pub.dev/packages/three_js_advanced_loaders) | ✅ | ✅ | ✅ |
+| [DDSLoader](https://github.com/Knightro63/three_js/blob/main/packages/three_js_advanced_loaders/lib/dds_loader.dart) | [three_js_advanced_loaders](https://pub.dev/packages/three_js_advanced_loaders) | ✅ | ✅ | ✅ |
+| DRACOLoader | | ❌ | ❌ | ❌ |
+| EXRLoader | | ⚠️ | ⚠️ | ⚠️ |
+| [FBXLoader](https://github.com/Knightro63/three_js/blob/main/packages/three_js_advanced_loaders/lib/fbx_loader.dart) | [three_js_advanced_loaders](https://pub.dev/packages/three_js_advanced_loaders) | ✅ | ✅ | ✅ |
+| [FontLoader](https://github.com/Knightro63/three_js/blob/main/packages/three_js_text/lib/loaders/font_loader.dart) | [three_js_text](https://pub.dev/packages/three_js_text) | ✅ | ✅ | ✅ |
+| [GCodeLoader](https://github.com/Knightro63/three_js/blob/main/packages/three_js_simple_loaders/lib/gcode_loder.dart) | [three_js_simple_loaders](https://pub.dev/packages/three_js_simple_loaders) | ✅ | ✅ | ✅ |
+| [GLTFLoader](https://github.com/Knightro63/three_js/blob/main/packages/three_js_advanced_loaders/lib/gltf/gltf_loader.dart) | [three_js_advanced_loaders](https://pub.dev/packages/three_js_advanced_loaders) | ✅ | ✅ | ✅ |
+| HDRCubeTextureLoader | | ⚠️ | ⚠️ | ⚠️ |
+| IESLoader | | ⚠️ | ⚠️ | ⚠️ |
+| KMZLoader | | ❌ | ❌ | ❌ |
+| KTX2Loader | | ❌ | ❌ | ❌ |
+| [KTXLoader](https://github.com/Knightro63/three_js/blob/main/packages/three_js_advanced_loaders/lib/ktx_loader.dart) | [three_js_advanced_loaders](https://pub.dev/packages/three_js_advanced_loaders) | ✅ | ✅ | ✅ |
+| LDrawLoader | | ⚠️ | ⚠️ | ⚠️ |
+| LottieLoader | | ❌ | ❌ | ❌ |
+| LUT3dlLoader | | ⚠️ | ⚠️ | ⚠️ |
+| [LUTCubeLoader](https://github.com/Knightro63/three_js/blob/main/packages/three_js_simple_loaders/lib/lut_cube_loder.dart) | [three_js_simple_loaders](https://pub.dev/packages/three_js_simple_loaders) | ✅ | ✅ | ✅ |
+| LUTImageLoader | | ⚠️ | ⚠️ | ⚠️ |
+| LWOLoader | | ❌ | ❌ | ❌ |
+| MaterialXLoader | | ❌ | ❌ | ❌ |
+| [MD2Loader](https://github.com/Knightro63/three_js/blob/main/packages/three_js_advanced_loaders/lib/md2/md2_loader.dart) | [three_js_advanced_loaders](https://pub.dev/packages/three_js_advanced_loaders) | ✅ | ✅ | ✅ |
+| MDDLoader | | ⚠️ | ⚠️ | ⚠️ |
+| [MTLLoader](https://github.com/Knightro63/three_js/blob/main/packages/three_js_simple_loaders/lib/obj/mtl_loder.dart) | [three_js_simple_loaders](https://pub.dev/packages/three_js_simple_loaders) | ✅ | ✅ | ✅ |
+| NRRDLoader | | ⚠️ | ⚠️ | ⚠️ |
+| [OBJLoader](https://github.com/Knightro63/three_js/blob/main/packages/three_js_simple_loaders/lib/obj/obj_loder.dart) | [three_js_simple_loaders](https://pub.dev/packages/three_js_simple_loaders) | ✅ | ✅ | ✅ |
+| [PCDLoader](https://github.com/Knightro63/three_js/blob/main/packages/three_js_advanced_loaders/lib/pcd_loader.dart) | [three_js_advanced_loaders](https://pub.dev/packages/three_js_advanced_loaders) | ✅ | ✅ | ✅ |
+| PBDLoader | | ⚠️ | ⚠️ | ⚠️ |
+| [PLYLoader](https://github.com/Knightro63/three_js/blob/main/packages/three_js_simple_loaders/lib/ply_loder.dart) | [three_js_simple_loaders](https://pub.dev/packages/three_js_simple_loaders) | ✅ | ✅ | ✅ |
+| PVRLoader | | ⚠️ | ⚠️ | ⚠️ |
+| [RGBELoader](https://github.com/Knightro63/three_js/blob/main/packages/three_js_advanced_loaders/lib/rgbe_loader.dart) | [three_js_advanced_loaders](https://pub.dev/packages/three_js_advanced_loaders) | ✅ | ✅ | ✅ |
+| RGBMLoader | | ⚠️ | ⚠️ | ⚠️ |
+| [STLLoader](https://github.com/Knightro63/three_js/blob/main/packages/three_js_simple_loaders/lib/stl_loder.dart) | [three_js_simple_loaders](https://pub.dev/packages/three_js_simple_loaders) | ✅ | ✅ | ✅ |
+| [SVGLoader](https://github.com/Knightro63/three_js/blob/main/packages/three_js_svg/lib/import/svg_loder.dart) | [three_js_svg](https://pub.dev/packages/three_js_svg) | ✅ | ✅ | ✅ |
+| TDSLoader | | ⚠️ | ⚠️ | ⚠️ |
+| [TGALoader](https://github.com/Knightro63/three_js/blob/main/packages/three_js_advanced_loaders/lib/tga_loader.dart) | [three_js_advanced_loaders](https://pub.dev/packages/three_js_advanced_loaders) | ✅ | ✅ | ✅ |
+| TIFFLoader | | ❌ | ❌ | ❌ |
+| [TTFLoader](https://github.com/Knightro63/three_js/blob/main/packages/three_js_text/lib/loaders/ttf_loader.dart) | [three_js_text](https://pub.dev/packages/three_js_text) | ✅ | ✅ | ✅ |
+| UltraHDRLoader | | ⚠️ | ⚠️ | ⚠️ |
+| [USDLoader](https://github.com/Knightro63/three_js/blob/main/packages/three_js_advanced_loaders/lib/usdz/usdz_loader.dart) | [three_js_advanced_loaders](https://pub.dev/packages/three_js_advanced_loaders) | ✅ | ✅ | ✅ |
+| [USDZLoader](https://github.com/Knightro63/three_js/blob/main/packages/three_js_advanced_loaders/lib/usdz/usdz_loader.dart) | [three_js_advanced_loaders](https://pub.dev/packages/three_js_advanced_loaders) | ✅ | ✅ | ✅ |
+| [VOXLoader](https://github.com/Knightro63/three_js/blob/main/packages/three_js_simple_loaders/lib/vox_loder.dart) | [three_js_simple_loaders](https://pub.dev/packages/three_js_simple_loaders) | ✅ | ✅ | ✅ |
+| VRMLoader | | ❌ | ❌ | ❌ |
+| VTKLoader | | ⚠️ | ⚠️ | ⚠️ |
+| [XYZLoader](https://github.com/Knightro63/three_js/blob/main/packages/three_js_simple_loaders/lib/xyz_loder.dart) | [three_js_simple_loaders](https://pub.dev/packages/three_js_simple_loaders) | ✅ | ✅ | ✅ |
+| | **Materials** | | | |
+| LDrawConditionalLineMaterial | | ❌ | ❌ | ❌ |
+| LDrawConditionalLineNodeMaterial | | ❌ | ❌ | ❌ |
+| [MeshGouraudMaterial](https://github.com/Knightro63/three_js/blob/main/packages/three_js_core/lib/materials/mesh_gouraud_loader.dart) | [three_js_core](https://pub.dev/packages/three_js_core) | ✅ | ✅ | ✅ |
+| MeshPostProcessingMaterial | | ⚠️ | ⚠️ | ⚠️ |
+| | **Math** | | | |
+| [Capsule](https://github.com/Knightro63/three_js/blob/main/packages/three_js_objects/lib/octree/capsule.dart) | [three_js_objects](https://pub.dev/packages/three_js_objects) | ✅ | ✅ | ✅ |
+| ColorConverter | | ⚠️ | ⚠️ | ⚠️ |
+| ColorSpaces | | ⚠️ | ⚠️ | ⚠️ |
+| [ConvexHull](https://github.com/Knightro63/three_js/blob/main/packages/three_js_geometry/lib/convex_hull.dart) | [three_js_geometry](https://pub.dev/packages/three_js_geometry) | ✅ | ✅ | ✅ |
+| [ImprovedNoise](https://github.com/Knightro63/three_js/blob/main/packages/three_js_objects/lib/noise/improved_noise.dart) | [three_js_objects](https://pub.dev/packages/three_js_objects) | ✅ | ✅ | ✅ |
+| Lut | | ⚠️ | ⚠️ | ⚠️ |
+| [MeshSurfaceSampler](https://github.com/Knightro63/three_js/blob/main/packages/three_js_helpers/lib/utils/mesh_surface_sampler.dart) | [three_js_helpers](https://pub.dev/packages/three_js_helpers) | ✅ | ✅ | ✅ |
+| OBB | | ⚠️ | ⚠️ | ⚠️ |
+| [Octree](https://github.com/Knightro63/three_js/blob/main/packages/three_js_objects/lib/octree/octree.dart) | [three_js_objects](https://pub.dev/packages/three_js_objects) | ✅ | ✅ | ✅ |
+| SimplexNoise | | ⚠️ | ⚠️ | ⚠️ |
+| | **Misc** | | | |
+| ConvexObjectBreaker | | ⚠️ | ⚠️ | ⚠️ |
+| GPUComputationRenderer | | ❗ | ❗ | ❗ |
+| [Gyroscope](https://github.com/Knightro63/three_js/blob/main/packages/three_js_particles/lib/gyroscope.dart) | [three_js_particles](https://pub.dev/packages/three_js_particles) | ✅ | ✅ | ✅ |
+| [MD2Character](https://github.com/Knightro63/three_js/blob/main/packages/three_js_advanced_loaders/lib/md2/md2_charcter.dart) | [three_js_advanced_loaders](https://pub.dev/packages/three_js_advanced_loaders) | ✅ | ✅ | ✅ |
+| [MD2Loader](https://github.com/Knightro63/three_js/blob/main/packages/three_js_advanced_loaders/lib/md2/md2_loader.dart) | [three_js_advanced_loaders](https://pub.dev/packages/three_js_advanced_loaders) | ✅ | ✅ | ✅ |
+| MD2CharacterComplex | | ❌ | ❌ | ❌ |
+| [MorphAnimMesh](https://github.com/Knightro63/three_js/blob/main/packages/three_js_advanced_loaders/lib/md2/morph_anim_mesh.dart) | [three_js_advanced_loaders](https://pub.dev/packages/three_js_advanced_loaders) | ✅ | ✅ | ✅ |
+| MorphBlendMesh | | ⚠️ | ⚠️ | ⚠️ |
+| ProgressiveLightMap | | ⚠️ | ⚠️ | ⚠️ |
+| ProgressiveLightMapGPU | | ❗ | ❗ | ❌❗ |
+| [RollerCoasterGeometry](https://github.com/Knightro63/three_js/blob/main/examples/lib/rollercoster/rollercoaster.dart) | | ✅ | ✅ | ✅ |
+| [TubePainter](https://github.com/Knightro63/three_js/blob/main/packages/three_js_objects/lib/tube_painter.dart) | [three_js_objects](https://pub.dev/packages/three_js_objects) | ✅ | ✅ | ✅ |
+| Volume | | ⚠️ | ⚠️ | ⚠️ |
+| VolumeSlice | | ❌ | ❌ | ❌ |
+| | **Modifers** | | | |
+| CurveModifier | | ❌ | ❌ | ❌ |
+| CurveModifierGPU | | ❌ | ❌ | ❌ |
+| [EdgeSplitModifier](https://github.com/Knightro63/three_js/blob/main/packages/three_js_modifers/lib/edge_split_modifier.dart) | [three_js_modifers](https://pub.dev/packages/three_js_modifers) | ✅ | ✅ | ✅ |
+| [SimplifyModifier](https://github.com/Knightro63/three_js/blob/main/packages/three_js_modifers/lib/simplify_modifer.dart) | [three_js_modifers](https://pub.dev/packages/three_js_modifers) | ✅ | ✅ | ✅ |
+| [TessellateModifier](https://github.com/Knightro63/three_js/blob/main/packages/three_js_modifers/lib/tesselate_modifer.dart) | [three_js_modifers](https://pub.dev/packages/three_js_modifers) | ✅ | ✅ | ✅ |
+| | **Objects** | | | |
+| GroundedSkybox | | ⚠️ | ⚠️ | ⚠️ |
+| [Lensflare](https://github.com/Knightro63/three_js/blob/main/packages/three_js_objects/lib/lens_flare.dart) | [three_js_objects](https://pub.dev/packages/three_js_objects) | ✅ | ✅ | ✅ |
+| LensflareMesh | | ❗ | ❗ | ❗ |
+| [MarchingCubes](https://github.com/Knightro63/three_js/blob/main/packages/three_js_objects/lib/marching_cubes.dart) | [three_js_objects](https://pub.dev/packages/three_js_objects) | ✅ | ✅ | ✅ |
+| [Reflector](https://github.com/Knightro63/three_js/blob/main/packages/three_js_objects/lib/water/reflector.dart) | [three_js_objects](https://pub.dev/packages/three_js_objects) | ✅ | ✅ | ✅ |
+| ReflectorForSSRPass | | ⚠️ | ⚠️ | ⚠️ |
+| [Refractor](https://github.com/Knightro63/three_js/blob/main/packages/three_js_objects/lib/water/refractor.dart) | [three_js_objects](https://pub.dev/packages/three_js_objects) | ✅ | ✅ | ✅ |
+| ShadowMesh | | ⚠️ | ⚠️ | ⚠️ |
+| [Sky](https://github.com/Knightro63/three_js/blob/main/packages/three_js_objects/lib/sky/sky.dart) | [three_js_objects](https://pub.dev/packages/three_js_objects) | ✅ | ✅ | ✅ |
+| SkyMesh | | ❗ | ❗ | ❗ |
+| Water | | ⚠️ | ⚠️ | ⚠️ |
+| [Water2](https://github.com/Knightro63/three_js/blob/main/packages/three_js_objects/lib/water/water2.dart) | [three_js_objects](https://pub.dev/packages/three_js_objects) | ✅ | ✅ | ✅ |
+| Water2Mesh | | ❗ | ❗ | ❗ |
+| WaterMesh | | ❗ | ❗ | ❗ |
+| | **Off Screen** | | | |
+| Jank | | ⚠️ | ⚠️ | ⚠️ |
+| OffScreen | | ❌ | ❌ | ❌ |
+| Scene | | ❌ | ❌ | ❌ |
+| | **Physics** | | | |
+| AmmoPhysics | | ❌ | ❌ | ❌ |
+| JoltPhysics | | ❌ | ❌ | ❌ |
+| RapierPhysics | | ❌ | ❌ | ❌ |
+| | **Post Porcessing** | | | |
+| [AfterimagePass](https://github.com/Knightro63/three_js/blob/main/packages/three_js_postprocessing/lib/post/afterimage_pass.dart) | [three_js_postprocessing](https://pub.dev/packages/three_js_postprocessing) | ✅ | ✅ | ✅ |
+| [BloomPass](https://github.com/Knightro63/three_js/blob/main/packages/three_js_postprocessing/lib/post/bloom_pass.dart) | [three_js_postprocessing](https://pub.dev/packages/three_js_postprocessing) | ✅ | ✅ | ✅ |
+| BokehPass | | ⚠️ | ⚠️ | ⚠️ |
+| ClearPass | | ⚠️ | ⚠️ | ⚠️ |
+| CubeTexturePass | | ⚠️ | ⚠️ | ⚠️ |
+| [DotScreenPass](https://github.com/Knightro63/three_js/blob/main/packages/three_js_postprocessing/lib/post/dot_screen_pass.dart) | [three_js_postprocessing](https://pub.dev/packages/three_js_postprocessing) | ✅ | ✅ | ✅ |
+| [EffectComposer](https://github.com/Knightro63/three_js/blob/main/packages/three_js_postprocessing/lib/post/effect_composer.dart) | [three_js_postprocessing](https://pub.dev/packages/three_js_postprocessing) | ✅ | ✅ | ✅ |
+| [FilmPass](https://github.com/Knightro63/three_js/blob/main/packages/three_js_postprocessing/lib/post/film_pass.dart) | [three_js_postprocessing](https://pub.dev/packages/three_js_postprocessing) | ✅ | ✅ | ✅ |
+| [FXAAPass](https://github.com/Knightro63/three_js/blob/main/packages/three_js_postprocessing/lib/post/fxaa_pass.dart) | [three_js_postprocessing](https://pub.dev/packages/three_js_postprocessing) | ✅ | ✅ | ✅ |
+| [GlitchPass](https://github.com/Knightro63/three_js/blob/main/packages/three_js_postprocessing/lib/post/glitch_pass.dart) | [three_js_postprocessing](https://pub.dev/packages/three_js_postprocessing) | ✅ | ✅ | ✅ |
+| GTAOPass | | ⚠️ | ⚠️ | ⚠️ |
+| HalftonePass | | ⚠️ | ⚠️ | ⚠️ |
+| [LUTPass](https://github.com/Knightro63/three_js/blob/main/packages/three_js_postprocessing/lib/post/lut_pass.dart) | [three_js_postprocessing](https://pub.dev/packages/three_js_postprocessing) | ✅ | ✅ | ✅ |
+| [MaskPass](https://github.com/Knightro63/three_js/blob/main/packages/three_js_postprocessing/lib/post/mask_pass.dart) | [three_js_postprocessing](https://pub.dev/packages/three_js_postprocessing) | ✅ | ✅ | ✅ |
+| OutlinePass | | ⚠️ | ⚠️ | ⚠️ |
+| [OutputPass](https://github.com/Knightro63/three_js/blob/main/packages/three_js_postprocessing/lib/post/output_pass.dart) | [three_js_postprocessing](https://pub.dev/packages/three_js_postprocessing) | ✅ | ✅ | ✅ |
+| [Pass](https://github.com/Knightro63/three_js/blob/main/packages/three_js_postprocessing/lib/post/pass.dart) | [three_js_postprocessing](https://pub.dev/packages/three_js_postprocessing) | ✅ | ✅ | ✅ |
+| [RenderPass](https://github.com/Knightro63/three_js/blob/main/packages/three_js_postprocessing/lib/post/render_pass.dart) | [three_js_postprocessing](https://pub.dev/packages/three_js_postprocessing) | ✅ | ✅ | ✅ |
+| RenderPixelatedPass | | ⚠️ | ⚠️ | ⚠️ |
+| RenderTransitionPass | | ⚠️ | ⚠️ | ⚠️ |
+| SAOPass | | ⚠️ | ⚠️ | ⚠️ |
+| SavePass | | ⚠️ | ⚠️ | ⚠️ |
+| [ShaderPass](https://github.com/Knightro63/three_js/blob/main/packages/three_js_postprocessing/lib/post/shader_pass.dart) | [three_js_postprocessing](https://pub.dev/packages/three_js_postprocessing) | ✅ | ✅ | ✅ |
+| [SMAAPass](https://github.com/Knightro63/three_js/blob/main/packages/three_js_postprocessing/lib/post/smaa_pass.dart) | [three_js_postprocessing](https://pub.dev/packages/three_js_postprocessing) | ✅ | ✅ | ✅ |
+| [SSAARenderPass](https://github.com/Knightro63/three_js/blob/main/packages/three_js_postprocessing/lib/post/ssaa_render_pass.dart) | [three_js_postprocessing](https://pub.dev/packages/three_js_postprocessing) | ✅ | ✅ | ✅ |
+| SSAOPass | | ⚠️ | ⚠️ | ⚠️ |
+| SSRPass | | ⚠️ | ⚠️ | ⚠️ |
+| [TAARenderPass](https://github.com/Knightro63/three_js/blob/main/packages/three_js_postprocessing/lib/post/taa_render_pass.dart) | [three_js_postprocessing](https://pub.dev/packages/three_js_postprocessing) | ✅ | ✅ | ✅ |
+| [TexturePass](https://github.com/Knightro63/three_js/blob/main/packages/three_js_postprocessing/lib/post/texture_pass.dart) | [three_js_postprocessing](https://pub.dev/packages/three_js_postprocessing) | ✅ | ✅ | ✅ |
+| [UnrealBloomPass](https://github.com/Knightro63/three_js/blob/main/packages/three_js_postprocessing/lib/post/unreal_bloom_pass.dart) | [three_js_postprocessing](https://pub.dev/packages/three_js_postprocessing) | ✅ | ✅ | ✅ |
+| | **Renderers** | | | |
+| CSS2DRenderer | | ❌ | ❌ | ❌ |
+| CSS3DRenderer | | ❌ | ❌ | ❌ |
+| [Projector](https://github.com/Knightro63/three_js/blob/main/packages/three_js_svg/lib/export/projector.dart) | [three_js_svg](https://pub.dev/packages/three_js_svg) | ✅ | ✅ | ✅ |
+| [SVGRenderer](https://github.com/Knightro63/three_js/blob/main/packages/three_js_svg/lib/export/svg_renderer.dart) | [three_js_svg](https://pub.dev/packages/three_js_svg) | ✅ | ✅ | ✅ |
+| | **Shaders** | | | |
+| ACESFilmicToneMappingShader | | ⚠️ | ⚠️ | ⚠️ |
+| [AfterimageShader](https://github.com/Knightro63/three_js/blob/main/packages/three_js_postprocessing/lib/shaders/afterimage_shader.dart) | [three_js_postprocessing](https://pub.dev/packages/three_js_postprocessing) | ✅ | ✅ | ✅ |
+| BasicShader | | ⚠️ | ⚠️ | ⚠️ |
+| [BleachBypassShader](https://github.com/Knightro63/three_js/blob/main/packages/three_js_postprocessing/lib/shaders/bleach_bypass_shader.dart) | [three_js_postprocessing](https://pub.dev/packages/three_js_postprocessing) | ✅ | ✅ | ✅ |
+| BlendShader | | ⚠️ | ⚠️ | ⚠️ |
+| BokehShader | | ⚠️ | ⚠️ | ⚠️ |
+| BokehShader2 | | ⚠️ | ⚠️ | ⚠️ |
+| BrightnessContrastShader | | ⚠️ | ⚠️ | ⚠️ |
+| ColorCorrectionShader | | ⚠️ | ⚠️ | ⚠️ |
+| ColorifyShader | | ⚠️ | ⚠️ | ⚠️ |
+| [ConvolutionShader](https://github.com/Knightro63/three_js/blob/main/packages/three_js_postprocessing/lib/shaders/convolution_shader.dart) | [three_js_postprocessing](https://pub.dev/packages/three_js_postprocessing) | ✅ | ✅ | ✅ |
+| [CopyShader](https://github.com/Knightro63/three_js/blob/main/packages/three_js_postprocessing/lib/shaders/copy_shader.dart) | [three_js_postprocessing](https://pub.dev/packages/three_js_postprocessing) | ✅ | ✅ | ✅ |
+| DepthLimitedBlurShader | | ⚠️ | ⚠️ | ⚠️ |
+| [DigitalGlitch](https://github.com/Knightro63/three_js/blob/main/packages/three_js_postprocessing/lib/shaders/digital_glitch.dart) | [three_js_postprocessing](https://pub.dev/packages/three_js_postprocessing) | ✅ | ✅ | ✅ |
+| DOFMipMapShader | | ⚠️ | ⚠️ | ⚠️ |
+| [DotScreenShader](https://github.com/Knightro63/three_js/blob/main/packages/three_js_postprocessing/lib/shaders/dot_screen_shader.dart) | [three_js_postprocessing](https://pub.dev/packages/three_js_postprocessing) | ✅ | ✅ | ✅ |
+| ExposureShader | | ⚠️ | ⚠️ | ⚠️ |
+| [FilmShader](https://github.com/Knightro63/three_js/blob/main/packages/three_js_postprocessing/lib/shaders/film_shader.dart) | [three_js_postprocessing](https://pub.dev/packages/three_js_postprocessing) | ✅ | ✅ | ✅ |
+| FocusShader | | ⚠️ | ⚠️ | ⚠️ |
+| FreiChenShader | | ⚠️ | ⚠️ | ⚠️ |
+| [FXAAShader](https://github.com/Knightro63/three_js/blob/main/packages/three_js_postprocessing/lib/shaders/fxaa_shader.dart) | [three_js_postprocessing](https://pub.dev/packages/three_js_postprocessing) | ✅ | ✅ | ✅ |
+| [GammaCorrectionShader](https://github.com/Knightro63/three_js/blob/main/packages/three_js_postprocessing/lib/shaders/gamma_correction_shader.dart) | [three_js_postprocessing](https://pub.dev/packages/three_js_postprocessing) | ✅ | ✅ | ✅ |
+| GodRaysDepthMaskShader | | ⚠️ | ⚠️ | ⚠️ |
+| GTAOShader | | ⚠️ | ⚠️ | ⚠️ |
+| HalftoneShader | | ⚠️ | ⚠️ | ⚠️ |
+| [HorizontalBlurShader](https://github.com/Knightro63/three_js/blob/main/packages/three_js_postprocessing/lib/shaders/horizontal_blur_shader.dart) | [three_js_postprocessing](https://pub.dev/packages/three_js_postprocessing) | ✅ | ✅ | ✅ |
+| HorizontalTiltShiftShader | | ⚠️ | ⚠️ | ⚠️ |
+| HueSaturationShader | | ⚠️ | ⚠️ | ⚠️ |
+| KaleidoShader | | ⚠️ | ⚠️ | ⚠️ |
+| [LuminosityHighPassShader](https://github.com/Knightro63/three_js/blob/main/packages/three_js_postprocessing/lib/shaders/luminosity_high_pass_shader.dart) | [three_js_postprocessing](https://pub.dev/packages/three_js_postprocessing) | ✅ | ✅ | ✅ |
+| [LuminosityShader](https://github.com/Knightro63/three_js/blob/main/packages/three_js_postprocessing/lib/shaders/luminosity_shader.dart) | [three_js_postprocessing](https://pub.dev/packages/three_js_postprocessing) | ✅ | ✅ | ✅ |
+| MirrorShader | | ⚠️ | ⚠️ | ⚠️ |
+| NormalMapShader | | ⚠️ | ⚠️ | ⚠️ |
+| [OutputShader](https://github.com/Knightro63/three_js/blob/main/packages/three_js_postprocessing/lib/shaders/outpass_shader.dart) | [three_js_postprocessing](https://pub.dev/packages/three_js_postprocessing) | ✅ | ✅ | ✅ |
+| PoissonDenoiseShader | | ⚠️ | ⚠️ | ⚠️ |
+| RGBShiftShader | | ⚠️ | ⚠️ | ⚠️ |
+| SAOShader | | ⚠️ | ⚠️ | ⚠️ |
+| SepiaShader | | ⚠️ | ⚠️ | ⚠️ |
+| [SMAAShader](https://github.com/Knightro63/three_js/blob/main/packages/three_js_postprocessing/lib/shaders/smaa_shader.dart) | [three_js_postprocessing](https://pub.dev/packages/three_js_postprocessing) | ✅ | ✅ | ✅ |
+| [SobelOperatorShader](https://github.com/Knightro63/three_js/blob/main/packages/three_js_postprocessing/lib/shaders/sobel_operator_shader.dart) | [three_js_postprocessing](https://pub.dev/packages/three_js_postprocessing) | ✅ | ✅ | ✅ |
+| SSAOShader | | ⚠️ | ⚠️ | ⚠️ |
+| SSRShader | | ⚠️ | ⚠️ | ⚠️ |
+| SubsurfaceScatteringShader | | ⚠️ | ⚠️ | ⚠️ |
+| TechnicolorShader | | ⚠️ | ⚠️ | ⚠️ |
+| ToonShader | | ⚠️ | ⚠️ | ⚠️ |
+| TriangleBlurShader | | ⚠️ | ⚠️ | ⚠️ |
+| [UnpackDepthRGBAShader](https://github.com/Knightro63/three_js/blob/main/packages/three_js_postprocessing/lib/shaders/unpack_depth_rgba_shader.dart) | [three_js_postprocessing](https://pub.dev/packages/three_js_postprocessing) | ✅ | ✅ | ✅ |
+| VelocityShader | | ⚠️ | ⚠️ | ⚠️ |
+| [VerticalBlurShader](https://github.com/Knightro63/three_js/blob/main/packages/three_js_postprocessing/lib/shaders/vertical_blur_shader.dart) | [three_js_postprocessing](https://pub.dev/packages/three_js_postprocessing) | ✅ | ✅ | ✅ |
+| VerticalTiltShiftShader | | ⚠️ | ⚠️ | ⚠️ |
+| [VignetteShader](https://github.com/Knightro63/three_js/blob/main/packages/three_js_postprocessing/lib/shaders/vignette_shader.dart) | [three_js_postprocessing](https://pub.dev/packages/three_js_postprocessing) | ✅ | ✅ | ✅ |
+| VolumeShader | | ⚠️ | ⚠️ | ⚠️ |
+| WaterRefractionShader | | ⚠️ | ⚠️ | ⚠️ |
+| | **Textures** | | | |
+| FlakesTexture | | ⚠️ | ⚠️ | ⚠️ |
+| | **Transpiler** | ❗ | ❗ | ❗ |
+| | **TSL** | ❗ | ❗ | ❗ |
+| | **Utils** | | | |
+| [BufferGeometryUtils](https://github.com/Knightro63/three_js/blob/main/packages/three_js_modifers/lib/buffergeometry_utils.dart) | [three_js_modifers](https://pub.dev/packages/three_js_modifers) | ✅ | ✅ | ✅ |
+| [CameraUtils](https://github.com/Knightro63/three_js/blob/main/packages/three_js_helpers/lib/utils/camera_utils.dart) | [three_js_helpers](https://pub.dev/packages/three_js_helpers) | ✅ | ✅ | ✅ |
+| GeometryCompressionUtils | | ⚠️ | ⚠️ | ⚠️ |
+| GeometryUtils | | ⚠️ | ⚠️ | ⚠️ |
+| LDrawUtils | | ⚠️ | ⚠️ | ⚠️ |
+| SceneOptimizer | | ⚠️ | ⚠️ | ⚠️ |
+| SceneUtils | | ⚠️ | ⚠️ | ⚠️ |
+| ShadowMapViewer | | ⚠️ | ⚠️ | ⚠️ |
+| ShadowMapViewerGPU | | ❗ | ❗ | ❗ |
+| [SkeletonUtils](https://github.com/Knightro63/three_js/blob/main/packages/three_js_helpers/lib/utils/skeleton_utils.dart) | [three_js_helpers](https://pub.dev/packages/three_js_helpers) | ✅ | ✅ | ✅ |
+| SortUtils | | ⚠️ | ⚠️ | ⚠️ |
+| UVsDebug | | ⚠️ | ⚠️ | ⚠️ |
+| WebGLTextureUtils | | ⚠️ | ⚠️ | ⚠️ |
+| WebGPUTextureUtils | | ❗ | ❗ | ❗ |
+| | **WebXR** | | | |
+| [ARButton](https://github.com/Knightro63/three_js/blob/main/packages/three_js_xr/lib/buttons/ar_button.dart) | [three_js_xr](https://pub.dev/packages/three_js_xr) | ✅ | ❌ | ❌ |
+| OculusHandModel | | ❌ | ❌ | ❌ |
+| OculusHandPointerModel | | ❌ | ❌ | ❌ |
+| Text2D | | ❌ | ❌ | ❌ |
+| [VRButton](https://github.com/Knightro63/three_js/blob/main/packages/three_js_xr/lib/buttons/vr_button.dart) | [three_js_xr](https://pub.dev/packages/three_js_xr) | ✅ | ❌ | ❌ |
+| [XRButton](https://github.com/Knightro63/three_js/blob/main/packages/three_js_xr/lib/buttons/xr_button.dart) | [three_js_xr](https://pub.dev/packages/three_js_xr) | ✅ | ❌ | ❌ |
+| [XRControllerModelFactory](https://github.com/Knightro63/three_js/blob/main/packages/three_js_xr/lib/models/hand/xr_controller_model_factory.dart) | [three_js_xr](https://pub.dev/packages/three_js_xr) | ✅ | ❌ | ❌ |
+| XREstimatedLight | | ❌ | ❌ | ❌ |
+| [XRHandMeshModel](https://github.com/Knightro63/three_js/blob/main/packages/three_js_xr/lib/models/hand/xr_hand_mesh_modle.dart) | [three_js_xr](https://pub.dev/packages/three_js_xr) | ✅ | ❌ | ❌ |
+| [XRHandModelFactory](https://github.com/Knightro63/three_js/blob/main/packages/three_js_xr/lib/models/hand/xr_hand_modle_factory.dart) | [three_js_xr](https://pub.dev/packages/three_js_xr) | ✅ | ❌ | ❌ |
+| [XRHandPrimitiveModel](https://github.com/Knightro63/three_js/blob/main/packages/three_js_xr/lib/models/hand/xr_hand_primitive_modle.dart) | [three_js_xr](https://pub.dev/packages/three_js_xr) | ✅ | ❌ | ❌ |
+| [XRPlanes](https://github.com/Knightro63/three_js/blob/main/packages/three_js_xr/lib/models/xr_planes.dart) | [three_js_xr](https://pub.dev/packages/three_js_xr) | ✅ | ❌ | ❌ |
diff --git a/packages/three_js_angle/lib/three_js.dart b/packages/three_js_angle/lib/three_js.dart
new file mode 100755
index 00000000..c185bbd1
--- /dev/null
+++ b/packages/three_js_angle/lib/three_js.dart
@@ -0,0 +1,14 @@
+library three_js;
+
+/// Export all the packages that relies on Three_JS to work
+export 'package:three_js_math/three_js_math.dart';
+export 'package:three_js_advanced_loaders/three_js_advanced_loaders.dart';
+export 'package:three_js_animations/three_js_animations.dart';
+export 'package:three_js_controls/three_js_controls.dart';
+export 'package:three_js_core/three_js_core.dart';
+export 'package:three_js_core_loaders/three_js_core_loaders.dart';
+export 'package:three_js_curves/three_js_curves.dart';
+export 'package:three_js_simple_loaders/three_js_simple_loaders.dart';
+export 'package:three_js_text/three_js_text.dart';
+export 'package:three_js_geometry/three_js_geometry.dart';
+export 'package:three_js_angle_renderer/three_js_angle_renderer.dart';
diff --git a/packages/three_js_angle/pubspec.yaml b/packages/three_js_angle/pubspec.yaml
new file mode 100755
index 00000000..f06ad5eb
--- /dev/null
+++ b/packages/three_js_angle/pubspec.yaml
@@ -0,0 +1,43 @@
+name: three_js_angle
+description: "Flutter package converted from threejs and three_dart to allow users to view, edit, and control 3D models."
+version: 0.0.1
+homepage: https://github.com/Knightro63/three_js/tree/main/packages/three_js_angle
+
+topics:
+ - threejs
+ - games
+ - animations
+ - flutter3d
+ - model3d
+
+environment:
+ sdk: '>=3.3.2 <4.0.0'
+ flutter: ">=1.17.0"
+
+dependencies:
+ flutter:
+ sdk: flutter
+ three_js_math: ^0.3.0
+ three_js_advanced_loaders: ^0.3.0
+ three_js_animations: ^0.3.0
+ three_js_controls: ^0.3.0
+ three_js_core: ^0.3.0
+ three_js_core_loaders: ^0.3.0
+ three_js_curves: ^0.3.0
+ three_js_simple_loaders: ^0.3.0
+ three_js_text: ^0.3.0
+ three_js_geometry: ^0.3.0
+ three_js_angle_renderer: ^0.0.1
+
+dev_dependencies:
+ flutter_test:
+ sdk: flutter
+ flutter_lints: ^5.0.0
+
+platforms:
+ android:
+ ios:
+ macos:
+ web:
+ windows:
+ linux:
\ No newline at end of file
diff --git a/packages/three_js_angle_renderer/.gitignore b/packages/three_js_angle_renderer/.gitignore
new file mode 100755
index 00000000..fbf43d48
--- /dev/null
+++ b/packages/three_js_angle_renderer/.gitignore
@@ -0,0 +1,143 @@
+# Miscellaneous
+*.class
+*.lock
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+renderers_old/
+renderers_new/
+# Visual Studio Code related
+.classpath
+.project
+.settings/
+.vscode/*
+.metadata
+
+*/macos
+*/web
+*/ios
+*/android
+*/linux
+*/windows
+
+# Flutter repo-specific
+/bin/cache/
+/bin/internal/bootstrap.bat
+/bin/internal/bootstrap.sh
+/bin/mingit/
+/dev/benchmarks/mega_gallery/
+/dev/bots/.recipe_deps
+/dev/bots/android_tools/
+/dev/devicelab/ABresults*.json
+/dev/docs/doc/
+/dev/docs/flutter.docs.zip
+/dev/docs/lib/
+/dev/docs/pubspec.yaml
+/dev/integration_tests/**/xcuserdata
+/dev/integration_tests/**/Pods
+/packages/flutter/coverage/
+version
+analysis_benchmark.json
+
+# packages file containing multi-root paths
+.packages.generated
+
+# Flutter/Dart/Pub related
+**/doc/api/
+.dart_tool/
+.flutter-plugins
+.flutter-plugins-dependencies
+**/generated_plugin_registrant.dart
+.packages
+.pub-preload-cache/
+.pub/
+build/
+flutter_*.png
+linked_*.ds
+unlinked.ds
+unlinked_spec.ds
+
+# Android related
+**/android/**/gradle-wrapper.jar
+.gradle/
+**/android/captures/
+**/android/gradlew
+**/android/gradlew.bat
+**/android/local.properties
+**/android/**/GeneratedPluginRegistrant.java
+**/android/key.properties
+*.jks
+
+# iOS/XCode related
+**/ios/**/*.mode1v3
+**/ios/**/*.mode2v3
+**/ios/**/*.moved-aside
+**/ios/**/*.pbxuser
+**/ios/**/*.perspectivev3
+**/ios/**/*sync/
+**/ios/**/.sconsign.dblite
+**/ios/**/.tags*
+**/ios/**/.vagrant/
+**/ios/**/DerivedData/
+**/ios/**/Icon?
+**/ios/**/Pods/
+**/ios/**/.symlinks/
+**/ios/**/profile
+**/ios/**/xcuserdata
+**/ios/.generated/
+**/ios/Flutter/.last_build_id
+**/ios/Flutter/App.framework
+**/ios/Flutter/Flutter.framework
+**/ios/Flutter/Flutter.podspec
+**/ios/Flutter/Generated.xcconfig
+**/ios/Flutter/ephemeral
+**/ios/Flutter/app.flx
+**/ios/Flutter/app.zip
+**/ios/Flutter/flutter_assets/
+**/ios/Flutter/flutter_export_environment.sh
+**/ios/ServiceDefinitions.json
+**/ios/Runner/GeneratedPluginRegistrant.*
+
+# macOS
+**/Flutter/ephemeral/
+**/Pods/
+**/macos/Flutter/GeneratedPluginRegistrant.swift
+**/macos/Flutter/ephemeral
+**/xcuserdata/
+
+# Windows
+**/windows/flutter/generated_plugin_registrant.cc
+**/windows/flutter/generated_plugin_registrant.h
+**/windows/flutter/generated_plugins.cmake
+
+# Linux
+**/linux/flutter/generated_plugin_registrant.cc
+**/linux/flutter/generated_plugin_registrant.h
+**/linux/flutter/generated_plugins.cmake
+
+# Coverage
+coverage/
+
+# Symbols
+app.*.symbols
+
+# Exceptions to above rules.
+!**/ios/**/default.mode1v3
+!**/ios/**/default.mode2v3
+!**/ios/**/default.pbxuser
+!**/ios/**/default.perspectivev3
+!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
+!/dev/ci/**/Gemfile.lock
+!.vscode/settings.json
\ No newline at end of file
diff --git a/packages/three_js_angle_renderer/CHANGELOG.md b/packages/three_js_angle_renderer/CHANGELOG.md
new file mode 100755
index 00000000..9b7b03ba
--- /dev/null
+++ b/packages/three_js_angle_renderer/CHANGELOG.md
@@ -0,0 +1,3 @@
+## 0.0.1
+
+* Initial release.
\ No newline at end of file
diff --git a/packages/three_js_angle_renderer/LICENSE b/packages/three_js_angle_renderer/LICENSE
new file mode 100755
index 00000000..383b4da5
--- /dev/null
+++ b/packages/three_js_angle_renderer/LICENSE
@@ -0,0 +1,21 @@
+The MIT License
+
+Copyright © 2010-2024 three.js authors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/packages/three_js_angle_renderer/README.md b/packages/three_js_angle_renderer/README.md
new file mode 100755
index 00000000..a918e082
--- /dev/null
+++ b/packages/three_js_angle_renderer/README.md
@@ -0,0 +1,21 @@
+# three_js_angle_renderer
+
+[](https://pub.dev/packages/three_js_angle_renderer)
+[](https://opensource.org/licenses/MIT)
+
+This is one of the renderers for three_js to allow users to view their 3D objects in their projects.
+
+## Usage
+
+This is a renderer for three_js using google's ANGLE.
+
+## Example
+
+Find the example for this API [here](https://github.com/Knightro63/three_js/tree/main/packages/three_js_angle_renderer/example/lib/main.dart).
+
+## Contributing
+
+Contributions are welcome.
+In case of any problems look at [existing issues](https://github.com/Knightro63/three_js/issues), if you cannot find anything related to your problem then open an issue.
+Create an issue before opening a [pull request](https://github.com/Knightro63/three_js/pulls) for non trivial fixes.
+In case of trivial fixes open a [pull request](https://github.com/Knightro63/three_js/pulls) directly.
diff --git a/packages/three_js_angle_renderer/example/.gitignore b/packages/three_js_angle_renderer/example/.gitignore
new file mode 100755
index 00000000..79c113f9
--- /dev/null
+++ b/packages/three_js_angle_renderer/example/.gitignore
@@ -0,0 +1,45 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.build/
+.buildlog/
+.history
+.svn/
+.swiftpm/
+migrate_working_dir/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# The .vscode folder contains launch configuration and tasks you configure in
+# VS Code which you may wish to be included in version control, so this line
+# is commented out by default.
+#.vscode/
+
+# Flutter/Dart/Pub related
+**/doc/api/
+**/ios/Flutter/.last_build_id
+.dart_tool/
+.flutter-plugins
+.flutter-plugins-dependencies
+.pub-cache/
+.pub/
+/build/
+
+# Symbolication related
+app.*.symbols
+
+# Obfuscation related
+app.*.map.json
+
+# Android Studio will place build artifacts here
+/android/app/debug
+/android/app/profile
+/android/app/release
diff --git a/packages/three_js_angle_renderer/example/README.md b/packages/three_js_angle_renderer/example/README.md
new file mode 100755
index 00000000..2b3fce4c
--- /dev/null
+++ b/packages/three_js_angle_renderer/example/README.md
@@ -0,0 +1,16 @@
+# example
+
+A new Flutter project.
+
+## Getting Started
+
+This project is a starting point for a Flutter application.
+
+A few resources to get you started if this is your first Flutter project:
+
+- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
+- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
+
+For help getting started with Flutter development, view the
+[online documentation](https://docs.flutter.dev/), which offers tutorials,
+samples, guidance on mobile development, and a full API reference.
diff --git a/packages/three_js_angle_renderer/example/lib/main.dart b/packages/three_js_angle_renderer/example/lib/main.dart
new file mode 100755
index 00000000..6b8099d1
--- /dev/null
+++ b/packages/three_js_angle_renderer/example/lib/main.dart
@@ -0,0 +1,114 @@
+import 'dart:async';
+import 'package:flutter/material.dart';
+import 'package:three_js_core/three_js_core.dart' as three;
+import 'package:three_js_math/three_js_math.dart' as tmath;
+import 'package:three_js_angle_renderer/three_js_angle_renderer.dart';
+import 'dart:math' as math;
+
+void main() {
+ runApp(const MyApp());
+}
+
+class MyApp extends StatelessWidget {
+ const MyApp({super.key});
+
+ // This widget is the root of your application.
+ @override
+ Widget build(BuildContext context) {
+ return MaterialApp(
+ theme: ThemeData(
+ colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
+ useMaterial3: true,
+ ),
+ home: const WebglGeometries(),
+ );
+ }
+}
+
+class WebglGeometries extends StatefulWidget {
+ const WebglGeometries({super.key});
+
+ @override
+ createState() => _State();
+}
+
+class _State extends State {
+ late ThreeJS threeJs;
+
+ @override
+ void initState() {
+ threeJs = ThreeJS(
+ onSetupComplete: (){setState(() {});},
+ setup: setup,
+ settings: Settings(
+ localClippingEnabled: true,
+ )
+ );
+ super.initState();
+ }
+ @override
+ void dispose() {
+ threeJs.dispose();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return threeJs.build();
+ }
+
+ int startTime = 0;
+
+ Future setup() async {
+ threeJs.camera = three.PerspectiveCamera(45, threeJs.width / threeJs.height, 1, 2000);
+ threeJs.camera.position.y = 400;
+
+ threeJs.scene = three.Scene();
+
+ three.Mesh object;
+
+ final ambientLight = three.AmbientLight(0xffffff, 0.8);
+ threeJs.scene.add(ambientLight);
+
+ final pointLight = three.PointLight(0xffffff, 0.8);
+ threeJs.camera.add(pointLight);
+ threeJs.scene.add(threeJs.camera);
+
+ final material = three.MeshPhongMaterial.fromMap({
+ "color": 0xffffff,
+ "side": tmath.DoubleSide,
+ "clipShadows": true
+ });
+ object = three.Mesh(three.SphereGeometry(75, 20, 10), material);
+ object.position.setValues(-300, 0, 200);
+ threeJs.scene.add(object);
+
+ object = three.Mesh(three.PlaneGeometry(100, 100, 4, 4), material);
+ object.position.setValues(-300, 0, 0);
+ threeJs.scene.add(object);
+
+ object = three.Mesh(three.BoxGeometry(100, 100, 100, 4, 4, 4), material);
+ object.position.setValues(-100, 0, 0);
+ threeJs.scene.add(object);
+
+
+ startTime = DateTime.now().millisecondsSinceEpoch;
+
+ threeJs.addAnimationEvent((dt){
+ final timer = DateTime.now().millisecondsSinceEpoch * 0.0001;
+
+ threeJs.camera.position.x = math.cos(timer) * 800;
+ threeJs.camera.position.z = math.sin(timer) * 800;
+ threeJs.camera.lookAt(threeJs.scene.position);
+
+ threeJs.scene.traverse((object) {
+ if (object is three.Mesh) {
+ object.rotation.x = timer * 5;
+ object.rotation.y = timer * 2.5;
+ }
+ });
+ });
+ }
+}
+
+
diff --git a/packages/three_js_angle_renderer/example/pubspec.yaml b/packages/three_js_angle_renderer/example/pubspec.yaml
new file mode 100755
index 00000000..475e1bd2
--- /dev/null
+++ b/packages/three_js_angle_renderer/example/pubspec.yaml
@@ -0,0 +1,33 @@
+name: example
+description: "A new Flutter project."
+publish_to: 'none' # Remove this line if you wish to publish to pub.dev
+version: 1.0.0+1
+
+environment:
+ sdk: '>=3.3.2 <4.0.0'
+
+dependencies:
+ flutter:
+ sdk: flutter
+ cupertino_icons: ^1.0.6
+ three_js_angle_renderer:
+ path: ../
+ three_js_math: ^0.2.0
+ three_js_core: ^0.2.0
+
+dev_dependencies:
+ flutter_test:
+ sdk: flutter
+ flutter_lints: ^3.0.0
+
+dependency_overrides:
+ flutter_angle:
+ path: ../../../../flutter_angle/flutter_angle
+ three_js_math:
+ path: ../../three_js_math
+ three_js_core:
+ path: ../../three_js_core
+
+# The following section is specific to Flutter packages.
+flutter:
+ uses-material-design: true
\ No newline at end of file
diff --git a/packages/three_js_angle_renderer/lib/angle/angle_animation.dart b/packages/three_js_angle_renderer/lib/angle/angle_animation.dart
new file mode 100755
index 00000000..0301405f
--- /dev/null
+++ b/packages/three_js_angle_renderer/lib/angle/angle_animation.dart
@@ -0,0 +1,36 @@
+part of three_webgl;
+
+class AngleAnimation {
+ dynamic context;
+ bool isAnimating = false;
+ dynamic animationLoop;
+ dynamic requestId;
+
+ AngleAnimation();
+
+ void onAnimationFrame(double time, int frame) {
+ animationLoop(time, frame);
+ requestId = context.requestAnimationFrame(onAnimationFrame);
+ }
+
+ void start() {
+ if (isAnimating == true) return;
+ if (animationLoop == null) return;
+
+ requestId = context.requestAnimationFrame(onAnimationFrame);
+ isAnimating = true;
+ }
+
+ void stop() {
+ context?.cancelAnimationFrame(requestId);
+ isAnimating = false;
+ }
+
+ void setAnimationLoop(callback) {
+ animationLoop = callback;
+ }
+
+ void setContext(value) {
+ context = value;
+ }
+}
diff --git a/packages/three_js_angle_renderer/lib/angle/angle_attributes.dart b/packages/three_js_angle_renderer/lib/angle/angle_attributes.dart
new file mode 100755
index 00000000..b2878c42
--- /dev/null
+++ b/packages/three_js_angle_renderer/lib/angle/angle_attributes.dart
@@ -0,0 +1,211 @@
+part of three_webgl;
+
+class AngleAttributes {
+ RenderingContext gl;
+ bool isWebGL2 = true;
+
+ WeakMap buffers = WeakMap();
+
+ AngleAttributes(this.gl);
+
+ Map createBuffer(dynamic attribute, int bufferType, {String? name}) {//BufferAttribute
+ final array = attribute.array;
+ final usage = attribute.usage;
+
+ dynamic type = WebGL.FLOAT;
+ int bytesPerElement = 4;
+
+ final buffer = gl.createBuffer();
+
+ gl.bindBuffer(bufferType, buffer);
+ gl.bufferData(bufferType, array, usage);
+
+ attribute.onUploadCallback?.call();
+
+ if (attribute is Float32BufferAttribute) {
+ type = WebGL.FLOAT;
+ bytesPerElement = Float32List.bytesPerElement;
+ }
+ else if (attribute is Float64BufferAttribute) {
+ console.error('WebGLAttributes: Unsupported data buffer format: Float64Array.');
+ }
+ else if (attribute is Float16BufferAttribute) {
+ if (isWebGL2) {
+ bytesPerElement = 2;
+ type = WebGL.HALF_FLOAT;
+ } else {
+ console.error('WebGLAttributes: Usage of Float16BufferAttribute requires WebGL2.');
+ }
+ } else if (attribute is Uint16BufferAttribute) {
+ bytesPerElement = Uint16List.bytesPerElement;
+ type = WebGL.UNSIGNED_SHORT;
+ } else if (attribute is Int16BufferAttribute) {
+ bytesPerElement = Int16List.bytesPerElement;
+
+ type = WebGL.SHORT;
+ } else if (attribute is Uint32BufferAttribute) {
+ bytesPerElement = Uint32List.bytesPerElement;
+
+ type = WebGL.UNSIGNED_INT;
+ } else if (attribute is Int32BufferAttribute) {
+ bytesPerElement = Int32List.bytesPerElement;
+ type = WebGL.INT;
+ } else if (attribute is Int8BufferAttribute) {
+ bytesPerElement = Int8List.bytesPerElement;
+ type = WebGL.BYTE;
+ } else if (attribute is Uint8BufferAttribute) {
+ bytesPerElement = Uint8List.bytesPerElement;
+ type = WebGL.UNSIGNED_BYTE;
+ }
+
+ return {
+ "buffer": buffer,
+ "type": type,
+ "bytesPerElement": bytesPerElement,
+ "array": array,
+ "version": attribute.version
+ };
+ }
+
+ void updateBuffer(Buffer buffer, attribute, int bufferType) {
+ final updateRange = attribute.updateRange;
+
+ gl.bindBuffer(bufferType, buffer);
+
+ if (updateRange!["count"] == -1) {
+ // Not using update ranges
+ gl.bufferSubData(bufferType, 0, attribute.array);
+ }
+ else {
+ console.info(" WebGLAttributes.dart gl.bufferSubData need debug confirm.... ");
+ gl.bufferSubData(bufferType, updateRange["offset"]! * attribute.itemSize, attribute.array);
+ updateRange["count"] = -1; // reset range
+ }
+ }
+
+ void updateBufferNew(Buffer buffer, attribute, int bufferType) {
+ final array = attribute.array;
+ final updateRanges = attribute.updateRanges;
+
+ gl.bindBuffer(bufferType, buffer);
+
+ if (updateRanges!["length"] == 0) {
+ // Not using update ranges
+ gl.bufferSubData(bufferType, 0, attribute.array);
+ }
+ // else {
+ // console.info(" WebGLAttributes.dart gl.bufferSubData need debug confirm.... ");
+ // gl.bufferSubData(bufferType, updateRange["offset"]! * attribute.itemSize, attribute.array);
+ // updateRange["count"] = -1; // reset range
+ // }
+
+ // print(updateRanges);
+
+ else{
+ updateRanges.sort( ( a, b ) => a.start - b.start );
+
+ int mergeIndex = 0;
+
+ for ( int i = 1; i < updateRanges.length; i ++ ) {
+ final previousRange = updateRanges[ mergeIndex ];
+ final range = updateRanges[ i ];
+
+ // We add one here to merge adjacent ranges. This is safe because ranges
+ // operate over positive integers.
+ if ( range.start <= previousRange.start + previousRange.count + 1 ) {
+ previousRange.count = math.max(
+ previousRange.count,
+ range.start + range.count - previousRange.start
+ );
+ }
+ else {
+ ++ mergeIndex;
+ updateRanges[ mergeIndex ] = range;
+ }
+ }
+ updateRanges.length = mergeIndex + 1;
+ for (int i = 0, l = updateRanges.length; i < l; i ++ ) {
+ final range = updateRanges[i];
+ Float32List f = Float32List.fromList(attribute.array.sublist(range.start,range.count) as List);
+ gl.bufferSubData(
+ bufferType,
+ range.start * array.bytesPerElement,
+ f,
+ );
+
+ //f.dispose();
+ }
+
+ attribute.clearUpdateRanges();
+ }
+
+ attribute.onUploadCallback?.call();
+ }
+
+ dynamic get(BaseBufferAttribute attribute) {
+ if (attribute is InterleavedBufferAttribute) {
+ return buffers.get(attribute.data);
+ }
+ else {
+ return buffers.get(attribute);
+ }
+ }
+ void dispose(){
+ final len = buffers.keys.toList();
+ for(int i = 0; i < len.length;i++){
+ if(len[i] is BufferAttribute){
+ (len[i] as BufferAttribute).dispose();
+ remove(len[i]);
+ }
+ else if(len[i] is TypedDataList){
+ remove(len[i]);
+ }
+ }
+ buffers.clear();
+ }
+ void remove(BufferAttribute attribute) {
+ if (attribute is InterleavedBufferAttribute) {
+ final data = buffers.get(attribute.data);
+
+ if (data != null) {
+ gl.deleteBuffer(data['buffer']);
+ buffers.delete(attribute.data);
+ }
+ } else {
+ final data = buffers.get(attribute);
+
+ if (data != null) {
+ gl.deleteBuffer(data["buffer"]);
+
+ buffers.delete(attribute);
+ }
+ }
+ }
+
+ void update(attribute, bufferType, {String? name}) {
+ if (attribute is GLBufferAttribute) {
+ final cached = buffers.get(attribute);
+ if (cached == null || cached["version"] < attribute.version) {
+ buffers.add(key: attribute, value: createBuffer(attribute, bufferType, name: name));
+ }
+ return;
+ }
+
+ if (attribute is InterleavedBufferAttribute) {
+ attribute = attribute.data;
+ }
+
+ final data = buffers.get(attribute);
+
+ if (data == null && attribute != null) {
+ buffers.add(
+ key: attribute,
+ value: createBuffer(attribute, bufferType, name: name)
+ );
+ }
+ else if(data?["version"] != null && data["version"] < attribute.version) {
+ updateBuffer(data["buffer"], attribute, bufferType);
+ data["version"] = attribute.version;
+ }
+ }
+}
diff --git a/packages/three_js_angle_renderer/lib/angle/angle_background.dart b/packages/three_js_angle_renderer/lib/angle/angle_background.dart
new file mode 100755
index 00000000..a0b5eeb8
--- /dev/null
+++ b/packages/three_js_angle_renderer/lib/angle/angle_background.dart
@@ -0,0 +1,234 @@
+part of three_webgl;
+
+final _e1 = Euler();
+final _m1 = Matrix4();
+
+class AngleBackground {
+ bool _didDispose = false;
+ AngleCubeMaps cubemaps;
+ AngleCubeUVMaps cubeuvmaps;
+ AngleState state;
+
+ AngleRenderer renderer;
+ AngleObjects objects;
+ bool alpha;
+ bool premultipliedAlpha;
+
+ Color clearColor = Color(0x000000);
+ double clearAlpha = 0;
+
+ Mesh? planeMesh;
+ Mesh? boxMesh;
+
+ dynamic currentBackground;
+ int currentBackgroundVersion = 0;
+ late int currentTonemapping;
+
+ AngleBackground(this.renderer, this.cubemaps, this.cubeuvmaps, this.state, this.objects, this.alpha, this.premultipliedAlpha) {
+ clearAlpha = alpha == true ? 0.0 : 1.0;
+ }
+
+ dynamic getBackground(Object3D? scene ) {
+ dynamic background = scene is Scene? scene.background : null;
+
+ if ( background != null && background is Texture ) {
+ final usePMREM = (scene as Scene).backgroundBlurriness > 0; // use PMREM if the user wants to blur the background
+ background = usePMREM ? cubeuvmaps.get(background) : cubemaps.get(background);
+ }
+
+ return background;
+ }
+
+ void render(Object3D scene) {
+ bool forceClear = false;
+ dynamic background = getBackground(scene);//scene is Scene ? scene.background : null;
+
+ if (background == null) {
+ setClear(clearColor, clearAlpha);
+ }
+ else if (background != null && background is Color) {
+ setClear(background, 1);
+ forceClear = true;
+ }
+
+ final environmentBlendMode = renderer.xr.getEnvironmentBlendMode();
+
+ if (environmentBlendMode == 'additive' ) {
+ state.buffers['color'].setClear( 0, 0, 0, 1, premultipliedAlpha );
+ }
+ else if (environmentBlendMode == 'alpha-blend' ) {
+ state.buffers['color'].setClear( 0, 0, 0, 0, premultipliedAlpha );
+ }
+
+ if (renderer.autoClear || forceClear) {
+ state.buffers['depth'].setTest( true );
+ state.buffers['depth'].setMask( true );
+ state.buffers['color'].setMask( true );
+
+ renderer.clear(renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil);
+ }
+ }
+
+ void addToRenderList(AngleRenderList renderList, Object3D scene) {
+ final background = getBackground( scene );
+
+ if ( background != null && ( background is CubeTexture || (background is Texture && background.mapping == CubeUVReflectionMapping)) ) {
+ if ( boxMesh == null ) {
+ boxMesh = Mesh(
+ BoxGeometry( 1, 1, 1 ),
+ ShaderMaterial.fromMap( {
+ 'name': 'BackgroundCubeMaterial',
+ 'uniforms': cloneUniforms( shaderLib['backgroundCube']['uniforms'] ),
+ 'vertexShader': shaderLib['backgroundCube']['vertexShader'],
+ 'fragmentShader': shaderLib['backgroundCube']['fragmentShader'],
+ 'side': BackSide,
+ 'depthTest': false,
+ 'depthWrite': false,
+ 'fog': false
+ })
+ );
+
+ boxMesh!.geometry?.deleteAttributeFromString( 'normal' );
+ boxMesh!.geometry?.deleteAttributeFromString( 'uv' );
+
+ boxMesh!.onBeforeRender = ({
+ renderer,
+ scene,
+ camera,
+ renderTarget,
+ mesh,
+ geometry,
+ material,
+ group
+ }) {
+ boxMesh!.matrixWorld.copyPosition(camera!.matrixWorld);
+ };
+
+ planeMesh?.material?.envMap = planeMesh?.material?.uniforms['envMap']['value'];
+ objects.update(boxMesh!);
+ }
+
+ (scene as Scene);
+ _e1.copy(scene.backgroundRotation);
+
+ // accommodate left-handed frame
+ _e1.x *= - 1; _e1.y *= - 1; _e1.z *= - 1;
+
+ if ( background is CubeTexture && !background.isRenderTargetTexture) {
+ // environment maps which are not cube render targets or PMREMs follow a different convention
+ _e1.y *= - 1;
+ _e1.z *= - 1;
+ }
+
+ boxMesh!.material!.uniforms['envMap']['value'] = background;
+ boxMesh!.material!.uniforms['flipEnvMap']['value'] = ( background is CubeTexture && !background.isRenderTargetTexture) ? - 1 : 1;
+ boxMesh!.material!.uniforms['backgroundBlurriness']['value'] = scene.backgroundBlurriness;
+ boxMesh!.material!.uniforms['backgroundIntensity']['value'] = scene.backgroundIntensity;
+ boxMesh!.material!.uniforms['backgroundRotation']['value'].setFromMatrix4( _m1.makeRotationFromEuler( _e1 ) );
+ boxMesh!.material!.toneMapped = ColorManagement.getTransfer( ColorSpace.fromString( background.colorSpace )) != SRGBTransfer;
+
+ if ( currentBackground != background ||
+ currentBackgroundVersion != background.version ||
+ currentTonemapping != renderer.toneMapping ) {
+ boxMesh!.material?.needsUpdate = true;
+
+ currentBackground = background;
+ currentBackgroundVersion = background.version;
+ currentTonemapping = renderer.toneMapping;
+ }
+
+ boxMesh!.layers.enableAll();
+
+ // push to the pre-sorted opaque render list
+ renderList.unshift(boxMesh!, boxMesh!.geometry, boxMesh!.material, 0, 0, null );
+
+ }
+ else if ( background != null && background is Texture ) {
+ if (planeMesh == null ) {
+
+ planeMesh = Mesh(
+ PlaneGeometry( 2, 2 ),
+ ShaderMaterial.fromMap( {
+ 'name': 'BackgroundMaterial',
+ 'uniforms': cloneUniforms( shaderLib['background']['uniforms'] ),
+ 'vertexShader': shaderLib['background']['vertexShader'],
+ 'fragmentShader': shaderLib['background']['fragmentShader'],
+ 'side': FrontSide,
+ 'depthTest': false,
+ 'depthWrite': false,
+ 'fog': false
+ } )
+ );
+
+ planeMesh!.geometry?.deleteAttributeFromString( 'normal' );
+ planeMesh!.material?.map = planeMesh!.material!.uniforms['t2D']['value'];
+
+ objects.update(planeMesh!);
+ }
+
+ planeMesh!.material?.uniforms['t2D']['value'] = background;
+ planeMesh!.material?.uniforms['backgroundIntensity']['value'] = (scene as Scene).backgroundIntensity;
+ planeMesh!.material?.toneMapped = ColorManagement.getTransfer( ColorSpace.fromString(background.colorSpace)) != SRGBTransfer;
+
+ if ( background.matrixAutoUpdate) {
+ background.updateMatrix();
+ }
+
+ planeMesh!.material?.uniforms['uvTransform']['value'].setFrom( background.matrix );
+
+ if ( currentBackground != background ||
+ currentBackgroundVersion != background.version ||
+ currentTonemapping != renderer.toneMapping ) {
+
+ planeMesh!.material?.needsUpdate = true;
+
+ currentBackground = background;
+ currentBackgroundVersion = background.version;
+ currentTonemapping = renderer.toneMapping;
+ }
+
+ planeMesh!.layers.enableAll();
+
+ // push to the pre-sorted opaque render list
+ renderList.unshift( planeMesh!, planeMesh!.geometry, planeMesh!.material, 0, 0, null );
+ }
+ }
+
+ void setClear(Color color, double alpha) {
+ state.buffers["color"].setClear(color.red, color.green, color.blue, alpha, premultipliedAlpha);
+ }
+
+ Color getClearColor() {
+ return clearColor;
+ }
+
+ void setClearColor(Color color, [double alpha = 1.0]) {
+ clearColor.setFrom(color);
+ clearAlpha = alpha;
+ setClear(clearColor, clearAlpha);
+ }
+
+ double getClearAlpha() {
+ return clearAlpha;
+ }
+
+ void setClearAlpha(double alpha) {
+ clearAlpha = alpha;
+ setClear(clearColor, clearAlpha);
+ }
+
+ void dispose(){
+ if(_didDispose) return;
+ _didDispose = true;
+ cubemaps.dispose();
+ state.dispose();
+ renderer.dispose();
+ planeMesh?.dispose();
+ boxMesh?.dispose();
+ objects.dispose();
+
+ if(currentBackground is Texture){
+ currentBackground.dispose();
+ }
+ }
+}
diff --git a/packages/three_js_angle_renderer/lib/angle/angle_binding_states.dart b/packages/three_js_angle_renderer/lib/angle/angle_binding_states.dart
new file mode 100755
index 00000000..92bbb629
--- /dev/null
+++ b/packages/three_js_angle_renderer/lib/angle/angle_binding_states.dart
@@ -0,0 +1,466 @@
+part of three_webgl;
+
+class AngleBindingStates {
+ bool _didDispose = false;
+ RenderingContext gl;
+ AngleAttributes attributes;
+
+ late int maxVertexAttributes;
+
+ dynamic extension;
+
+ late Map defaultState;
+ late Map currentState;
+ late Map bindingStates;
+
+ bool forceUpdate = false;
+
+ AngleBindingStates(
+ this.gl,
+ this.attributes,
+ ) {
+ maxVertexAttributes = gl.getParameter(WebGL.MAX_VERTEX_ATTRIBS);
+ bindingStates = {};
+ defaultState = createBindingState(null);
+ currentState = defaultState;
+ }
+
+ void setup(
+ Object3D object,
+ Material material,
+ AngleProgram program,
+ BufferGeometry geometry,
+ BufferAttribute? index,
+ ) {
+ bool updateBuffers = false;
+
+ final state = getBindingState(geometry, program, material);
+
+ if (currentState != state) {
+ currentState = state;
+ bindVertexArrayObject(currentState["object"]);
+ }
+
+ updateBuffers = needsUpdate(object, geometry, program, index);
+
+ if (updateBuffers) saveCache(object, geometry, program, index);
+
+
+ if (index != null) {
+ attributes.update(index, WebGL.ELEMENT_ARRAY_BUFFER);
+ }
+
+ if (updateBuffers || forceUpdate) {
+ forceUpdate = false;
+
+ setupVertexAttributes(object, material, program, geometry);
+
+ if (index != null) {
+ final buf = attributes.get(index)["buffer"];
+ gl.bindBuffer(WebGL.ELEMENT_ARRAY_BUFFER, buf);
+ }
+ }
+ }
+
+ VertexArrayObject createVertexArrayObject() {
+ return gl.createVertexArray();
+ }
+
+ void bindVertexArrayObject(VertexArrayObject? vao) {
+ if (vao != null) {
+ return gl.bindVertexArray(vao);
+ }
+ else {
+ console.warning(" WebGLBindingStates.dart bindVertexArrayObject VAO is null");
+ return;
+ }
+ }
+
+ void deleteVertexArrayObject(vao) {
+ return gl.deleteVertexArray(vao);
+ }
+
+ getBindingState(
+ BufferGeometry geometry,
+ program,
+ Material material,
+ ) {
+ final wireframe = (material.wireframe == true);
+
+ Map? programMap = bindingStates[geometry.id];
+
+ if (programMap == null) {
+ programMap = {};
+ bindingStates[geometry.id] = programMap;
+ }
+
+ Map? stateMap = programMap[program.id];
+
+ if (stateMap == null) {
+ stateMap = {};
+ programMap[program.id] = stateMap;
+ }
+
+ Map? state = stateMap[wireframe];
+
+ if (state == null) {
+ state = createBindingState(createVertexArrayObject());
+ stateMap[wireframe] = state;
+ }
+
+ return state;
+ }
+
+ Map createBindingState(VertexArrayObject? vao) {
+ final newAttributes = List.filled(maxVertexAttributes, 0);
+ final enabledAttributes = List.filled(maxVertexAttributes, 0);
+ final attributeDivisors = List.filled(maxVertexAttributes, 0);
+
+ // for (int i = 0; i < maxVertexAttributes; i++) {
+ // newAttributes[i] = 0;
+ // enabledAttributes[i] = 0;
+ // attributeDivisors[i] = 0;
+ // }
+
+ return {
+ // for backward compatibility on non-VAO support browser
+ "geometry": null,
+ "program": null,
+ "wireframe": false,
+
+ "newAttributes": newAttributes,
+ "enabledAttributes": enabledAttributes,
+ "attributeDivisors": attributeDivisors,
+ "object": vao,
+ "attributes": {},
+ "index": null
+ };
+ }
+
+ bool needsUpdate(Object3D object, BufferGeometry geometry, AngleProgram program, BufferAttribute? index) {
+ final cachedAttributes = currentState["attributes"];
+ final geometryAttributes = geometry.attributes;
+ int attributesNum = 0;
+ final programAttributes = program.getAttributes();
+ for (final name in programAttributes.keys) {
+ AttributeLocations programAttribute = programAttributes[name]!;
+
+ if (programAttribute.location.id >= 0) {
+ final cachedAttribute = cachedAttributes[name];
+ BufferAttribute? geometryAttribute = geometryAttributes[name];
+
+ if (geometryAttribute == null) {
+ if (name == 'instanceMatrix' && object.instanceMatrix != null) geometryAttribute = object.instanceMatrix;
+ if (name == 'instanceColor' && object.instanceColor != null) geometryAttribute = object.instanceColor;
+ }
+
+ if (cachedAttribute == null) return true;
+
+ if (cachedAttribute["attribute"] != geometryAttribute) return true;
+
+ if (geometryAttribute != null && cachedAttribute["data"] != geometryAttribute.data) return true;
+
+ attributesNum++;
+ }
+ }
+
+ if (currentState["attributesNum"] != attributesNum) return true;
+ if (currentState["index"] != index) return true;
+ return false;
+ }
+
+ void saveCache(object, BufferGeometry geometry, AngleProgram program, BufferAttribute? index) {
+ final cache = {};
+ final attributes = geometry.attributes;
+ int attributesNum = 0;
+
+ final programAttributes = program.getAttributes();
+
+ for (final name in programAttributes.keys) {
+ AttributeLocations programAttribute = programAttributes[name]!;
+
+ if (programAttribute.location.id >= 0) {
+ BufferAttribute? attribute = attributes[name];
+
+ if (attribute == null) {
+ if (name == 'instanceMatrix' && object.instanceMatrix != null) attribute = object.instanceMatrix;
+ if (name == 'instanceColor' && object.instanceColor != null) attribute = object.instanceColor;
+ }
+
+ final data = {};
+ data["attribute"] = attribute;
+
+ if (attribute != null && attribute.data != null) {
+ data["data"] = attribute.data;
+ }
+
+ cache[name] = data;
+
+ attributesNum++;
+ }
+ }
+
+ currentState["attributes"] = cache;
+ currentState["attributesNum"] = attributesNum;
+
+ currentState["index"] = index;
+ }
+
+ void initAttributes() {
+ final newAttributes = currentState["newAttributes"];
+
+ for (int i = 0, il = newAttributes.length; i < il; i++) {
+ newAttributes[i] = 0;
+ }
+ }
+
+ void enableAttribute(int attribute) {
+ enableAttributeAndDivisor(attribute, 0);
+ }
+
+ void enableAttributeAndDivisor(int attribute, int meshPerAttribute) {
+ final newAttributes = currentState["newAttributes"];
+ final enabledAttributes = currentState["enabledAttributes"];
+ final attributeDivisors = currentState["attributeDivisors"];
+
+ newAttributes[attribute] = 1;
+
+ if (enabledAttributes[attribute] == 0) {
+ gl.enableVertexAttribArray(attribute);
+ enabledAttributes[attribute] = 1;
+ }
+
+ if (attributeDivisors[attribute] != meshPerAttribute) {
+ gl.vertexAttribDivisor(attribute, meshPerAttribute);
+ attributeDivisors[attribute] = meshPerAttribute;
+ }
+ }
+
+ void disableUnusedAttributes() {
+ final newAttributes = currentState["newAttributes"];
+ final enabledAttributes = currentState["enabledAttributes"];
+
+ for (int i = 0, il = enabledAttributes.length; i < il; i++) {
+ if (enabledAttributes[i] != newAttributes[i]) {
+ gl.disableVertexAttribArray(i);
+ enabledAttributes[i] = 0;
+ }
+ }
+ }
+
+ void vertexAttribPointer(int index, int size, int type, bool normalized, int stride, int offset, bool integer) {
+ if (integer){
+ gl.vertexAttribIPointer(index, size, type, stride, offset);
+ } else {
+ gl.vertexAttribPointer(index, size, type, normalized, stride, offset);
+ }
+ }
+
+ void setupVertexAttributes(
+ Object3D object,
+ Material material,
+ AngleProgram program,
+ BufferGeometry geometry,
+ ) {
+ initAttributes();
+
+ final geometryAttributes = geometry.attributes;
+
+ final programAttributes = program.getAttributes();
+
+ final materialDefaultAttributeValues = material.defaultAttributeValues;
+
+ for (final name in programAttributes.keys) {
+ final programAttribute = programAttributes[name];
+
+ if (programAttribute!.location.id >= 0) {
+ BufferAttribute? geometryAttribute = geometryAttributes[name];
+
+ if (geometryAttribute == null) {
+ if (name == 'instanceMatrix' && object is InstancedMesh) {
+ geometryAttribute = object.instanceMatrix;
+ }
+ if (name == 'instanceColor' && object is InstancedMesh && object.instanceColor != null) {
+ geometryAttribute = object.instanceColor;
+ }
+ }
+
+ if (geometryAttribute != null) {
+ final normalized = geometryAttribute.normalized;
+ final size = geometryAttribute.itemSize;
+
+ final attribute = attributes.get(geometryAttribute);
+
+ // TODO Attribute may not be available on context restore
+
+ if (attribute == null) {
+ console.warning("WebGLBindingState setupVertexAttributes name: $name attribute == null ");
+ continue;
+ }
+
+ final buffer = attribute["buffer"];
+ final type = attribute["type"];
+ final bytesPerElement = attribute["bytesPerElement"];
+
+ final integer = ( type == WebGL.INT || type == WebGL.UNSIGNED_INT) && geometryAttribute.gpuType == IntType;
+ if (geometryAttribute is InterleavedBufferAttribute) {
+ final data = geometryAttribute.data;
+ final stride = data?.stride;
+ final offset = geometryAttribute.offset;
+
+ if (data != null && data is InstancedInterleavedBuffer) {
+ for (int i = 0; i < programAttribute.locationSize; i++) {
+ enableAttributeAndDivisor(programAttribute.location.id + i, data.meshPerAttribute);
+ }
+
+ if (object is! InstancedMesh && geometry.maxInstanceCount == null) {
+ geometry.maxInstanceCount = data.meshPerAttribute * data.count;
+ }
+ }
+ else {
+ for (int i = 0; i < programAttribute.locationSize; i++) {
+ enableAttribute(programAttribute.location.id + i);
+ }
+ }
+
+ gl.bindBuffer(WebGL.ARRAY_BUFFER, buffer);
+
+ for (int i = 0; i < programAttribute.locationSize; i++) {
+ vertexAttribPointer(
+ programAttribute.location.id + i,
+ size ~/ programAttribute.locationSize,
+ type,
+ normalized,
+ (stride! * bytesPerElement).toInt(),
+ ((offset + (size ~/ programAttribute.locationSize) * i) * bytesPerElement).toInt(),
+ integer
+ );
+ }
+ }
+ else {
+ if (geometryAttribute is InstancedBufferAttribute) {
+ for (int i = 0; i < programAttribute.locationSize; i++) {
+ enableAttributeAndDivisor(programAttribute.location.id + i, geometryAttribute.meshPerAttribute);
+ }
+ geometry.maxInstanceCount ??= geometryAttribute.meshPerAttribute * geometryAttribute.count;
+ }
+ else {
+ for (int i = 0; i < programAttribute.locationSize; i++) {
+ enableAttribute(programAttribute.location.id + i);
+ }
+ }
+
+ gl.bindBuffer(WebGL.ARRAY_BUFFER, buffer);
+ for (int i = 0; i < programAttribute.locationSize; i++) {
+ vertexAttribPointer(
+ programAttribute.location.id + i,
+ size ~/ programAttribute.locationSize,
+ type,
+ normalized,
+ (size * bytesPerElement).toInt(),
+ ((size ~/ programAttribute.locationSize) * i * bytesPerElement).toInt(),
+ integer
+ );
+ }
+ }
+ }
+ else if (materialDefaultAttributeValues != null) {
+ final value = materialDefaultAttributeValues[name];
+
+ if (value != null) {
+ switch (value.length) {
+ case 2:
+ gl.vertexAttrib2fv(programAttribute.location.id, value);
+ break;
+ case 3:
+ gl.vertexAttrib3fv(programAttribute.location.id, value);
+ break;
+ case 4:
+ gl.vertexAttrib4fv(programAttribute.location.id, value);
+ break;
+ default:
+ gl.vertexAttrib1fv(programAttribute.location.id, value);
+ }
+ }
+ }
+ }
+ }
+
+ disableUnusedAttributes();
+ }
+
+ void dispose() {
+ if(_didDispose) return;
+ _didDispose = true;
+ reset();
+
+ for ( final geometryId in bindingStates.keys ) {
+ final programMap = bindingStates[ geometryId ];
+ for ( final programId in programMap.keys ) {
+ final stateMap = programMap[ programId ];
+ for ( final wireframe in stateMap.keys) {
+ deleteVertexArrayObject( stateMap[ wireframe ]['object'] );
+ }
+ stateMap.clear();
+ }
+ programMap.clear();
+ }
+
+ bindingStates.clear();
+ attributes.dispose();
+ defaultState.clear();
+ currentState.clear();
+ attributes.dispose();
+ }
+
+ void releaseStatesOfGeometry(BufferGeometry geometry) {
+ if (bindingStates[geometry.id] == null) return;
+
+ final programMap = bindingStates[geometry.id];
+ for (final programId in programMap.keys) {
+ final stateMap = programMap[programId];
+ for (final wireframe in stateMap.keys) {
+ deleteVertexArrayObject(stateMap[wireframe]["object"]);
+ }
+ stateMap.clear();
+ }
+ programMap.clear();
+
+ bindingStates.remove(geometry.id);
+ }
+
+ void releaseStatesOfProgram(program) {
+ console.info(" WebGLBindingStates releaseStatesOfProgram ");
+
+ for (final geometryId in bindingStates.keys ) {
+ final programMap = bindingStates[ geometryId ];
+
+ if ( programMap[ program.id ] == null ) continue;
+ final stateMap = programMap[ program.id ];
+
+ for ( final wireframe in stateMap.keys ) {
+ deleteVertexArrayObject( stateMap[ wireframe ]['object'] );
+ }
+ (stateMap as Map).clear();
+ (programMap as Map).remove(program.id);
+ }
+ }
+
+ void reset() {
+ resetDefaultState();
+ forceUpdate = true;
+
+ if (currentState == defaultState) return;
+
+ currentState = defaultState;
+ bindVertexArrayObject(currentState["object"]);
+ }
+
+ // for backward-compatilibity
+
+ void resetDefaultState() {
+ defaultState["geometry"] = null;
+ defaultState["program"] = null;
+ defaultState["wireframe"] = false;
+ }
+}
diff --git a/packages/three_js_angle_renderer/lib/angle/angle_buffer_renderer.dart b/packages/three_js_angle_renderer/lib/angle/angle_buffer_renderer.dart
new file mode 100755
index 00000000..e8996f91
--- /dev/null
+++ b/packages/three_js_angle_renderer/lib/angle/angle_buffer_renderer.dart
@@ -0,0 +1,107 @@
+part of three_webgl;
+
+class BaseAngleBufferRenderer {
+ void setIndex(value) {
+ throw (" BaseAngleBufferRenderer.setIndex value: $value ");
+ }
+
+ void render(int start, int count) {
+ throw (" BaseWebGLBufferRenderer.render start: $start $count ");
+ }
+
+ void renderInstances(int start, int count, int primcount) {
+ throw (" BaseWebGLBufferRenderer.renderInstances start: $start $count primcount: $primcount ");
+ }
+
+ void setMode(value) {
+ throw (" BaseWebGLBufferRenderer.setMode value: $value ");
+ }
+
+ void renderMultiDraw(List starts, List counts, int drawCount) {
+ throw (" BaseWebGLBufferRenderer.renderMultiDraw not supported ");
+ }
+
+ void renderMultiDrawInstances(List starts, List counts, int drawCount, List primcount ) {
+ throw (" BaseWebGLBufferRenderer.renderMultiDrawInstances not supported ");
+ }
+}
+
+class AngleBufferRenderer extends BaseAngleBufferRenderer {
+ bool _didDispose = false;
+ RenderingContext gl;
+ bool isWebGL2 = true;
+ dynamic mode;
+ AngleExtensions extensions;
+ AngleInfo info;
+
+ AngleBufferRenderer(this.gl, this.extensions, this.info);
+
+ void dispose(){
+ if(_didDispose) return;
+ _didDispose = true;
+ extensions.dispose();
+ info.dispose();
+ }
+
+ @override
+ void setMode(value) {
+ mode = value;
+ }
+
+ @override
+ void render(int start, int count) {
+ gl.drawArrays(mode, start, count);
+ info.update(count, mode, 1);
+ }
+
+ @override
+ void renderInstances(int start, int count, int primcount) {
+ if (primcount == 0) return;
+ gl.drawArraysInstanced(mode, start, count, primcount);
+ info.update(count, mode, primcount);
+ }
+
+ @override
+ void renderMultiDraw(List starts, List counts, int drawCount) {
+ if ( drawCount == 0 ) return;
+ final extension = extensions.get( 'WEBGL_multi_draw' );
+
+ if ( extension == null ) {
+ for (int i = 0; i < drawCount; i ++ ) {
+ render(starts[i], counts[i]);
+ }
+ }
+ else {
+ extension.multiDrawArraysWEBGL( mode, starts, 0, counts, 0, drawCount );
+ int elementCount = 0;
+ for (int i = 0; i < drawCount; i ++ ) {
+ elementCount += counts[i];
+ }
+
+ info.update( elementCount, mode, 1 );
+ }
+ }
+
+ @override
+ void renderMultiDrawInstances(List starts, List counts, int drawCount, List primcount ) {
+ if ( drawCount == 0 ) return;
+ final extension = extensions.get( 'WEBGL_multi_draw' );
+
+ if ( extension == null ) {
+ for (int i = 0; i < starts.length; i ++ ) {
+ renderInstances(starts[i], counts[i], primcount[i]);
+ }
+ }
+ else {
+ extension.multiDrawArraysInstancedWEBGL( mode, starts, 0, counts, 0, primcount, 0, drawCount );
+
+ int elementCount = 0;
+ for (int i = 0; i < drawCount; i ++ ) {
+ elementCount += counts[ i ];
+ }
+ for (int i = 0; i < primcount.length; i ++ ) {
+ info.update(elementCount, mode, primcount[i]);
+ }
+ }
+ }
+}
diff --git a/packages/three_js_angle_renderer/lib/angle/angle_capabilities.dart b/packages/three_js_angle_renderer/lib/angle/angle_capabilities.dart
new file mode 100755
index 00000000..6375c40f
--- /dev/null
+++ b/packages/three_js_angle_renderer/lib/angle/angle_capabilities.dart
@@ -0,0 +1,95 @@
+part of three_webgl;
+
+class AngleCapabilities extends Capabilities {
+ bool _didDispose = false;
+
+ AngleRendererParameters parameters;
+ RenderingContext gl;
+ AngleExtensions extensions;
+ AngleUtils utils;
+
+ AngleCapabilities(this.gl, this.extensions, this.parameters, this.utils) {
+ precision = parameters.precision.name;
+
+ maxPrecision = getMaxPrecision(precision);
+ if (maxPrecision != precision) {
+ console.warning('AngleRenderer: $precision not supported, using $maxPrecision instead.');
+ precision = maxPrecision;
+ }
+
+ logarithmicDepthBuffer = parameters.logarithmicDepthBuffer == true;
+ reverseDepthBuffer = parameters.reverseDepthBuffer == true && extensions.has( 'EXT_clip_control' );
+
+ maxTextures = gl.getParameter(WebGL.MAX_TEXTURE_IMAGE_UNITS);
+ maxVertexTextures = gl.getParameter(WebGL.MAX_VERTEX_TEXTURE_IMAGE_UNITS);
+ maxTextureSize = gl.getParameter(WebGL.MAX_TEXTURE_SIZE);
+ maxCubemapSize = gl.getParameter(WebGL.MAX_CUBE_MAP_TEXTURE_SIZE);
+
+ maxAttributes = gl.getParameter(WebGL.MAX_VERTEX_ATTRIBS);
+ maxVertexUniforms = gl.getParameter(WebGL.MAX_VERTEX_UNIFORM_VECTORS);
+ maxVaryings = gl.getParameter(WebGL.MAX_VARYING_VECTORS);
+ maxFragmentUniforms = gl.getParameter(WebGL.MAX_FRAGMENT_UNIFORM_VECTORS);
+
+ vertexTextures = maxVertexTextures > 0;
+
+ maxSamples = gl.getParameter(WebGL.MAX_SAMPLES);
+ }
+
+ num getMaxAnisotropy() {
+ if (maxAnisotropy != null) return maxAnisotropy!;
+
+ final extension = extensions.get('EXT_texture_filter_anisotropic');
+
+ if (extension != null) {
+ maxAnisotropy = gl.getParameter(WebGL.MAX_TEXTURE_MAX_ANISOTROPY_EXT);
+ } else {
+ maxAnisotropy = 0;
+ }
+
+ return maxAnisotropy!;
+ }
+
+ bool textureFormatReadable(int textureFormat ) {
+ if ( textureFormat != RGBAFormat && utils.convert( textureFormat ) != gl.getParameter( WebGL.IMPLEMENTATION_COLOR_READ_FORMAT ) ) {
+ return false;
+ }
+ return true;
+ }
+
+ bool textureTypeReadable(int textureType ) {
+ final halfFloatSupportedByExt = ( textureType == HalfFloatType ) && ( extensions.has( 'EXT_color_buffer_half_float' ) || extensions.has( 'EXT_color_buffer_float' ) );
+
+ if ( textureType != UnsignedByteType && utils.convert( textureType ) != gl.getParameter( WebGL.IMPLEMENTATION_COLOR_READ_TYPE ) && // Edge and Chrome Mac < 52 (#9513)
+ textureType != FloatType && ! halfFloatSupportedByExt ) {
+ return false;
+ }
+
+ return true;
+ }
+ String getMaxPrecision([String? precision]) {
+ if ( precision == 'highp' ) {
+ if ( gl.getShaderPrecisionFormat( WebGL.VERTEX_SHADER, WebGL.HIGH_FLOAT ).precision > 0 &&
+ gl.getShaderPrecisionFormat( WebGL.FRAGMENT_SHADER, WebGL.HIGH_FLOAT ).precision > 0 ) {
+ return 'highp';
+ }
+
+ precision = 'mediump';
+ }
+
+ if ( precision == 'mediump' ) {
+ if ( gl.getShaderPrecisionFormat( WebGL.VERTEX_SHADER, WebGL.MEDIUM_FLOAT ).precision > 0 &&
+ gl.getShaderPrecisionFormat( WebGL.FRAGMENT_SHADER, WebGL.MEDIUM_FLOAT ).precision > 0 ) {
+ return 'mediump';
+ }
+ }
+
+ return 'lowp';
+ }
+
+ void dispose(){
+ if(_didDispose) return;
+ _didDispose = true;
+ //parameters.clear();
+ extensions.dispose();
+ }
+}
diff --git a/packages/three_js_angle_renderer/lib/angle/angle_clipping.dart b/packages/three_js_angle_renderer/lib/angle/angle_clipping.dart
new file mode 100755
index 00000000..7b63b7ef
--- /dev/null
+++ b/packages/three_js_angle_renderer/lib/angle/angle_clipping.dart
@@ -0,0 +1,138 @@
+part of three_webgl;
+
+class AngleClipping {
+ bool _didDispose = false;
+ AngleProperties properties;
+
+ Matrix3 viewNormalMatrix = Matrix3.identity();
+ Plane plane = Plane();
+ int numGlobalPlanes = 0;
+
+ bool localClippingEnabled = false;
+ bool renderingShadows = false;
+
+ dynamic globalState;
+
+ Map uniform = {"value": null, "needsUpdate": false};
+
+ int numPlanes = 0;
+ int numIntersection = 0;
+
+ AngleClipping(this.properties);
+
+ void dispose(){
+ if(_didDispose) return;
+ _didDispose = true;
+ properties.dispose();
+ uniform.clear();
+ }
+
+ bool init(List planes, bool enableLocalClipping) {
+ final enabled = planes.isNotEmpty ||
+ enableLocalClipping ||
+ // enable state of previous frame - the clipping code has to
+ // run another frame in order to reset the state:
+ numGlobalPlanes != 0 ||
+ localClippingEnabled;
+
+ localClippingEnabled = enableLocalClipping;
+
+ numGlobalPlanes = planes.length;
+
+ return enabled;
+ }
+
+ void beginShadows() {
+ renderingShadows = true;
+ projectPlanes();
+ }
+
+ void endShadows() {
+ renderingShadows = false;
+ }
+
+ void setGlobalState(List planes,Camera camera ) {
+ globalState = projectPlanes( planes, camera, 0 );
+ }
+
+
+ void setState(Material material, Camera camera, bool useCache) {
+ final planes = material.clippingPlanes;
+ final clipIntersection = material.clipIntersection;
+ final clipShadows = material.clipShadows;
+
+ final materialProperties = properties.get(material);
+
+ if (!localClippingEnabled || planes == null || planes.isEmpty || renderingShadows && !clipShadows) {
+ if (renderingShadows) {
+ projectPlanes();
+ }
+ else {
+ resetGlobalState();
+ }
+ }
+ else {
+ final nGlobal = renderingShadows ? 0 : numGlobalPlanes;
+ final lGlobal = nGlobal * 4;
+
+ List? dstArray = materialProperties["clippingState"];
+
+ uniform["value"] = dstArray; // ensure unique state
+
+ dstArray = projectPlanes(planes, camera, lGlobal, useCache);
+
+ for (int i = 0; i != lGlobal; ++i) {
+ dstArray?[i] = globalState[i];
+ }
+
+ materialProperties["clippingState"] = dstArray;
+ numIntersection = clipIntersection ? numPlanes : 0;
+ numPlanes += nGlobal;
+ }
+ }
+
+ void resetGlobalState() {
+ if (uniform["value"] != globalState) {
+ uniform["value"] = globalState;
+ uniform["needsUpdate"] = numGlobalPlanes > 0;
+ }
+
+ numPlanes = numGlobalPlanes;
+ numIntersection = 0;
+ }
+
+ List? projectPlanes([List? planes, Camera? camera, int dstOffset = 0, bool skipTransform = false]) {
+ final nPlanes = planes != null ? planes.length : 0;
+ List? dstArray;
+
+ if (nPlanes != 0) {
+ dstArray = uniform["value"];
+
+ if (!skipTransform || dstArray == null) {
+ final flatSize = dstOffset + nPlanes * 4;
+ final viewMatrix = camera?.matrixWorldInverse ?? Matrix4.identity();
+
+ viewNormalMatrix.getNormalMatrix(viewMatrix);
+
+ if (dstArray == null || dstArray.length < flatSize) {
+ dstArray = List.filled(flatSize, 0.0);
+ }
+
+ for (int i = 0, i4 = dstOffset; i != nPlanes; ++i, i4 += 4) {
+ plane..copyFrom(planes![i])..applyMatrix4(viewMatrix, viewNormalMatrix);
+
+ plane.normal.copyIntoArray(dstArray, i4);
+ dstArray[i4 + 3] = plane.constant;
+ }
+ }
+
+ uniform["value"] = dstArray;
+ uniform["needsUpdate"] = true;
+ }
+
+ numPlanes = nPlanes;
+ numIntersection = 0;
+
+ return dstArray;
+ }
+}
diff --git a/packages/three_js_angle_renderer/lib/angle/angle_cube_maps.dart b/packages/three_js_angle_renderer/lib/angle/angle_cube_maps.dart
new file mode 100755
index 00000000..f0d3374f
--- /dev/null
+++ b/packages/three_js_angle_renderer/lib/angle/angle_cube_maps.dart
@@ -0,0 +1,74 @@
+part of three_webgl;
+
+class AngleCubeMaps {
+ bool _didDispose = false;
+ AngleRenderer renderer;
+ WeakMap cubemaps = WeakMap();
+
+ AngleCubeMaps(this.renderer);
+
+ Texture mapTextureMapping(Texture texture, int? mapping) {
+ if (mapping == EquirectangularReflectionMapping) {
+ texture.mapping = CubeReflectionMapping;
+ }
+ else if (mapping == EquirectangularRefractionMapping) {
+ texture.mapping = CubeRefractionMapping;
+ }
+ return texture;
+ }
+
+ Texture? get(Texture? texture) {
+ if (texture != null && !texture.isRenderTargetTexture) {
+ final mapping = texture.mapping;
+
+ if (mapping == EquirectangularReflectionMapping || mapping == EquirectangularRefractionMapping) {
+ if (cubemaps.has(texture)) {
+ final cubemap = cubemaps.get(texture).texture;
+ return mapTextureMapping(cubemap, texture.mapping);
+ }
+ else {
+ final image = texture.image;
+
+ if (image != null && image.height > 0) {
+ final renderTarget = CubeRenderTarget(image.height ~/ 2);
+ renderTarget.fromEquirectangularTexture(renderer, texture);
+ cubemaps.add(key: texture, value: renderTarget);
+
+ texture.addEventListener('dispose', onTextureDispose);
+
+ return mapTextureMapping(renderTarget.texture, texture.mapping);
+ }
+ else {
+ // image not yet ready. try the conversion next frame
+ return null;
+ }
+ }
+ }
+ }
+
+ return texture;
+ }
+
+ void onTextureDispose(event) {
+ final texture = event.target;
+
+ texture.removeEventListener('dispose', onTextureDispose);
+
+ final cubemap = cubemaps.get(texture);
+
+ if (cubemap != null) {
+ cubemaps.delete(texture);
+ cubemap.dispose();
+ }
+ }
+
+ void dispose() {
+ if(_didDispose) return;
+ _didDispose = true;
+ for(final key in cubemaps.keys){
+ cubemaps[key].dispose();
+ }
+ cubemaps.clear();
+ renderer.dispose();
+ }
+}
diff --git a/packages/three_js_angle_renderer/lib/angle/angle_cube_uv_maps.dart b/packages/three_js_angle_renderer/lib/angle/angle_cube_uv_maps.dart
new file mode 100755
index 00000000..e5b5064d
--- /dev/null
+++ b/packages/three_js_angle_renderer/lib/angle/angle_cube_uv_maps.dart
@@ -0,0 +1,102 @@
+part of three_webgl;
+
+class AngleCubeUVMaps {
+ bool _didDispose = false;
+ WeakMap cubeUVmaps = WeakMap();
+ AngleRenderer renderer;
+ PMREMGenerator? pmremGenerator;
+
+ AngleCubeUVMaps(this.renderer);
+
+ Texture? get(Texture? texture) {
+ if (texture != null) {
+ final mapping = texture.mapping;
+
+ bool isEquirectMap = (mapping == EquirectangularReflectionMapping || mapping == EquirectangularRefractionMapping);
+ bool isCubeMap = (mapping == CubeReflectionMapping || mapping == CubeRefractionMapping);
+
+ // equirect/cube map to cubeUV conversion
+ if (isEquirectMap || isCubeMap) {
+ RenderTarget? renderTarget = cubeUVmaps.get( texture );
+ final currentPMREMVersion = renderTarget != null ? renderTarget.texture.pmremVersion : 0;
+
+ if (texture.isRenderTargetTexture && texture.pmremVersion != currentPMREMVersion) {
+ if ( pmremGenerator == null ) pmremGenerator = new PMREMGenerator( renderer );
+
+ renderTarget = isEquirectMap ? pmremGenerator?.fromEquirectangular( texture, renderTarget ) : pmremGenerator?.fromCubemap( texture, renderTarget );
+ renderTarget?.texture.pmremVersion = texture.pmremVersion;
+
+ cubeUVmaps.set( texture, renderTarget );
+
+ return renderTarget?.texture;
+ }
+ else {
+ if (renderTarget != null) {
+ return renderTarget.texture;
+ }
+ else {
+ final image = texture.image;
+
+ if ((isEquirectMap && image != null && image.height > 0) ||
+ (isCubeMap && image != null && isCubeTextureComplete(image))) {
+ pmremGenerator ??= PMREMGenerator(renderer);
+
+ renderTarget = isEquirectMap ? pmremGenerator!.fromEquirectangular(texture) : pmremGenerator!.fromCubemap(texture);
+ cubeUVmaps.set(texture, renderTarget);
+
+ texture.addEventListener('dispose', onTextureDispose);
+
+ return renderTarget.texture;
+ }
+ else {
+ // image not yet ready. try the conversion next frame
+
+ return null;
+ }
+ }
+ }
+ }
+ }
+
+ return texture;
+ }
+
+ bool isCubeTextureComplete(image) {
+ int count = 0;
+ const length = 6;
+
+ for (int i = 0; i < length; i++) {
+ if (image[i] != null) count++;
+ }
+
+ return count == length;
+ }
+
+ void onTextureDispose(event) {
+ final texture = event.target;
+ texture.removeEventListener('dispose', onTextureDispose);
+
+ final cubemapUV = cubeUVmaps.get(texture);
+
+ if (cubemapUV != null) {
+ cubeUVmaps.delete(texture);
+ cubemapUV.dispose();
+ }
+ }
+
+ void dispose() {
+ if(_didDispose) return;
+ _didDispose = true;
+ for(final key in cubeUVmaps.keys){
+ cubeUVmaps[key].dispose();
+ }
+ cubeUVmaps.clear();
+
+ //if (pmremGenerator != null) {
+ pmremGenerator?.dispose();
+ pmremGenerator = null;
+ //}
+
+ renderer.dispose();
+ }
+}
diff --git a/packages/three_js_angle_renderer/lib/angle/angle_extensions.dart b/packages/three_js_angle_renderer/lib/angle/angle_extensions.dart
new file mode 100755
index 00000000..7d20814c
--- /dev/null
+++ b/packages/three_js_angle_renderer/lib/angle/angle_extensions.dart
@@ -0,0 +1,127 @@
+part of three_webgl;
+
+class AngleExtensions {
+ Map extensions = {};
+ RenderingContext gl;
+
+ AngleExtensions(this.gl);
+
+ void dispose(){
+ extensions.clear();
+ }
+
+ dynamic getExtension(String name) {
+ return _has(name);
+ }
+
+ void init() {
+ getExtension( 'EXT_color_buffer_float' );
+ getExtension( 'WEBGL_clip_cull_distance' );
+ getExtension( 'OES_texture_float_linear' );
+ getExtension( 'EXT_color_buffer_half_float' );
+ getExtension( 'WEBGL_multisampled_render_to_texture' );
+ getExtension( 'WEBGL_render_shared_exponent' );
+ }
+
+ dynamic _has(String name) {
+ if (kIsWeb) {
+ return hasForWeb(name);
+ }
+ else {
+ return hasForApp(name);
+ }
+ }
+
+ bool has(String name) {
+ if (kIsWeb) {
+ return hasForWeb(name) != null;
+ }
+ else {
+ return hasForApp(name) != null;
+ }
+ }
+
+ dynamic hasForWeb(String name) {
+ if (extensions[name] != null) {
+ return extensions[name];
+ }
+
+ dynamic extension;
+
+ switch (name) {
+ case 'WEBGL_depth_texture':
+ extension = gl.getExtension('WEBGL_depth_texture') ??
+ gl.getExtension('MOZ_WEBGL_depth_texture') ??
+ gl.getExtension('WEBKIT_WEBGL_depth_texture');
+ break;
+
+ case 'EXT_texture_filter_anisotropic':
+ extension = gl.getExtension('EXT_texture_filter_anisotropic') ??
+ gl.getExtension('MOZ_EXT_texture_filter_anisotropic') ??
+ gl.getExtension('WEBKIT_EXT_texture_filter_anisotropic');
+ break;
+
+ case 'WEBGL_compressed_texture_s3tc':
+ extension = gl.getExtension('WEBGL_compressed_texture_s3tc') ??
+ gl.getExtension('MOZ_WEBGL_compressed_texture_s3tc') ??
+ gl.getExtension('WEBKIT_WEBGL_compressed_texture_s3tc');
+ break;
+
+ case 'WEBGL_compressed_texture_pvrtc':
+ extension = gl.getExtension('WEBGL_compressed_texture_pvrtc') ??
+ gl.getExtension('WEBKIT_WEBGL_compressed_texture_pvrtc');
+ break;
+
+ default:
+ extension = gl.getExtension(name);
+ }
+
+ extensions[name] = extension;
+
+ return extension;
+ }
+
+ dynamic hasForApp(name) {
+ if (extensions.keys.isEmpty) {
+ List ex = gl.getExtension(name) as List? ?? [];
+ extensions = {};
+ for (String element in ex) {
+ extensions[element] = element;
+ }
+ }
+
+ Map names = {
+ "EXT_color_buffer_float": "GL_EXT_color_buffer_float",
+ "EXT_texture_filter_anisotropic": "GL_EXT_texture_filter_anisotropic",
+ "EXT_color_buffer_half_float": "GL_EXT_color_buffer_half_float",
+ "GL_OES_texture_compression_astc": "GL_OES_texture_compression_astc",
+ "GL_KHR_texture_compression_astc_ldr": "GL_KHR_texture_compression_astc_ldr",
+ "GL_KHR_texture_compression_astc_hdr": "GL_KHR_texture_compression_astc_hdr",
+ "GL_KHR_texture_compression_astc_sliced_3d": "GL_KHR_texture_compression_astc_sliced_3d",
+ "GL_EXT_texture_compression_astc_decode_mode": "GL_EXT_texture_compression_astc_decode_mode",
+ "GL_EXT_texture_compression_astc_decode_mode_rgb9e5": "GL_EXT_texture_compression_astc_decode_mode_rgb9e5"
+ };
+
+ String n = names[name] ?? name;
+
+ // print(" has for app : ${name} ");
+ // developer.log( extensions.keys.toList().toString() );
+
+ if (extensions.containsKey(n)) {
+ return extensions[n];//s.containsKey(n);
+ }
+ else {
+ return null;
+ }
+ }
+
+ dynamic get(String name) {
+ dynamic extension = getExtension(name);
+
+ if (extension == null) {
+ console.warning('WebGLExtensions.get: $name extension not supported.');
+ }
+
+ return extension;
+ }
+}
diff --git a/packages/three_js_angle_renderer/lib/angle/angle_geometries.dart b/packages/three_js_angle_renderer/lib/angle/angle_geometries.dart
new file mode 100755
index 00000000..eadaaaf5
--- /dev/null
+++ b/packages/three_js_angle_renderer/lib/angle/angle_geometries.dart
@@ -0,0 +1,160 @@
+part of three_webgl;
+
+class AngleGeometries {
+ bool _didDispose = false;
+ RenderingContext gl;
+ AngleAttributes attributes;
+ AngleInfo info;
+ AngleBindingStates bindingStates;
+
+ Map geometries = {};
+ final wireframeAttributes = WeakMap();
+
+ AngleGeometries(this.gl, this.attributes, this.info, this.bindingStates);
+
+ void onGeometryDispose(Event event) {
+ final geometry = event.target;
+
+ if (geometry.index != null) {
+ attributes.remove(geometry.index);
+ }
+
+ for (String name in geometry.attributes.keys) {
+ attributes.remove(geometry.attributes[name]);
+ }
+
+ for (final name in geometry.morphAttributes.keys) {
+ final array = geometry.morphAttributes[ name ];
+
+ for (int i = 0, l = array.length; i < l; i ++ ) {
+ attributes.remove( array[ i ] );
+ }
+ }
+
+ geometry.removeEventListener('dispose', onGeometryDispose);
+
+ geometries.remove(geometry.id);
+
+ final attribute = wireframeAttributes.get(geometry);
+
+ if (attribute != null) {
+ attributes.remove(attribute);
+ wireframeAttributes.delete(geometry);
+ }
+
+ bindingStates.releaseStatesOfGeometry(geometry);
+
+ if (geometry is InstancedBufferGeometry) {
+ // geometry.remove("maxInstanceCount");
+ geometry.maxInstanceCount = null;
+ }
+
+ //
+
+ info.memory["geometries"] = info.memory["geometries"]! - 1;
+ }
+
+ BufferGeometry get(BufferGeometry geometry) {
+ if (geometries[geometry.id] == true) return geometry;
+
+ geometry.addEventListener('dispose', onGeometryDispose);
+
+ geometries[geometry.id] = true;
+
+ info.memory["geometries"] = info.memory["geometries"]! + 1;
+
+ return geometry;
+ }
+
+ void update(BufferGeometry geometry) {
+ final geometryAttributes = geometry.attributes;
+
+ for (final name in geometryAttributes.keys) {
+ attributes.update(geometryAttributes[name], WebGL.ARRAY_BUFFER);
+ }
+ }
+
+ void updateWireframeAttribute(BufferGeometry geometry) {
+ List indices = [];
+
+ final geometryIndex = geometry.index;
+ final geometryPosition = geometry.attributes["position"];
+ int version = 0;
+
+ if (geometryIndex != null) {
+ final array = geometryIndex.array;
+ version = geometryIndex.version;
+ for (int i = 0, l = array.length; i < l; i += 3) {
+ final a = array[i + 0].toInt();
+ final b = array[i + 1].toInt();
+ final c = array[i + 2].toInt();
+
+ indices.addAll([a, b, b, c, c, a]);
+ }
+ }
+ else if( geometryPosition != null ){
+ final array = geometryPosition.array;
+ version = geometryPosition.version;
+
+ for (int i = 0, l = (array.length ~/ 3) - 1; i < l; i += 3) {
+ final a = i + 0;
+ final b = i + 1;
+ final c = i + 2;
+
+ indices.addAll([a, b, b, c, c, a]);
+ }
+ }
+ else{
+ return;
+ }
+
+ BufferAttribute attribute;
+ final max = indices.getMaxValue();
+ if (max != null && max > 65535) {
+ attribute = Uint32BufferAttribute.fromList(indices, 1);
+ }
+ else {
+ attribute = Uint16BufferAttribute.fromList(indices, 1);
+ }
+
+ attribute.version = version;
+
+ // Updating index buffer in VAO now. See WebGLBindingStates
+
+ final previousAttribute = wireframeAttributes.get(geometry);
+ if (previousAttribute != null) attributes.remove(previousAttribute);
+ wireframeAttributes.set(geometry, attribute);
+ }
+
+ BufferAttribute? getWireframeAttribute(BufferGeometry geometry) {
+ final currentAttribute = wireframeAttributes.get(geometry);
+
+ if (currentAttribute != null) {
+ final geometryIndex = geometry.index;
+ if (geometryIndex != null) {
+ // if the attribute is obsolete, create a new one
+ if (currentAttribute.version < geometryIndex.version) {
+ updateWireframeAttribute(geometry);
+ }
+ }
+ }
+ else {
+ updateWireframeAttribute(geometry);
+ }
+ return wireframeAttributes.get(geometry);
+ }
+
+ void dispose() {
+ if(_didDispose) return;
+ _didDispose = true;
+ for(final key in wireframeAttributes.keys){
+ (wireframeAttributes[key] as BufferAttribute).dispose();
+ }
+
+ wireframeAttributes.clear();
+ geometries.clear();
+ attributes.dispose();
+ info.dispose();
+ bindingStates.dispose();
+ }
+}
diff --git a/packages/three_js_angle_renderer/lib/angle/angle_indexed_buffer_renderer.dart b/packages/three_js_angle_renderer/lib/angle/angle_indexed_buffer_renderer.dart
new file mode 100755
index 00000000..a0d8420b
--- /dev/null
+++ b/packages/three_js_angle_renderer/lib/angle/angle_indexed_buffer_renderer.dart
@@ -0,0 +1,79 @@
+part of three_webgl;
+
+class AngleIndexedBufferRenderer extends BaseAngleBufferRenderer {
+ bool _didDispose = false;
+ dynamic mode;
+ dynamic type;
+ late int bytesPerElement;
+ RenderingContext gl;
+ AngleExtensions extensions;
+ AngleInfo info;
+
+ AngleIndexedBufferRenderer(this.gl, this.extensions, this.info);
+
+ void dispose(){
+ if(_didDispose) return;
+ _didDispose = true;
+ extensions.dispose();
+ info.dispose();
+ }
+
+ @override
+ void setMode(value) {
+ mode = value;
+ }
+
+ @override
+ void setIndex(value) {
+ type = value["type"];
+ bytesPerElement = value["bytesPerElement"];
+ }
+
+ @override
+ void render(int start, int count) {
+ gl.drawElements(mode, count, type, start * bytesPerElement);
+ info.update(count, mode, 1);
+ }
+
+ @override
+ void renderInstances(int start, int count, int primcount) {
+ if (primcount == 0) return;
+ gl.drawElementsInstanced(mode, count, type, start * bytesPerElement, primcount);
+ info.update(count, mode, primcount);
+ }
+
+ void renderMultiDraw(List starts,List counts,int drawCount ) {
+ if ( drawCount == 0 ) return;
+ final extension = extensions.get( 'WEBGL_multi_draw' );
+
+ extension.multiDrawElementsWEBGL( mode, counts, 0, type, starts, 0, drawCount );
+ int elementCount = 0;
+ for ( int i = 0; i < drawCount; i ++ ) {
+ elementCount += counts[ i ];
+ }
+ info.update( elementCount, mode, 1 );
+ }
+
+ void renderMultiDrawInstances(List starts,List counts,int drawCount,List primcount ) {
+ if ( drawCount == 0 ) return;
+ final extension = extensions.get( 'WEBGL_multi_draw' );
+
+ if ( extension == null ) {
+ for (int i = 0; i < starts.length; i ++ ) {
+ renderInstances( starts[ i ] ~/ bytesPerElement, counts[ i ], primcount[ i ] );
+ }
+ }
+ else {
+ extension.multiDrawElementsInstancedWEBGL( mode, counts, 0, type, starts, 0, primcount, 0, drawCount );
+
+ int elementCount = 0;
+ for (int i = 0; i < drawCount; i ++ ) {
+ elementCount += counts[ i ];
+ }
+
+ for (int i = 0; i < primcount.length; i ++ ) {
+ info.update( elementCount, mode, primcount[ i ] );
+ }
+ }
+ }
+}
diff --git a/packages/three_js_angle_renderer/lib/angle/angle_info.dart b/packages/three_js_angle_renderer/lib/angle/angle_info.dart
new file mode 100755
index 00000000..a5c013c7
--- /dev/null
+++ b/packages/three_js_angle_renderer/lib/angle/angle_info.dart
@@ -0,0 +1,51 @@
+part of three_webgl;
+
+class AngleInfo {
+ RenderingContext gl;
+
+ Map memory = {
+ "geometries": 0,
+ "textures": 0
+ };
+
+ Map render = {
+ "frame": 0.0,
+ "calls": 0.0,
+ "triangles": 0.0,
+ "points": 0.0,
+ "lines": 0.0
+ };
+
+ dynamic programs;
+ bool autoReset = true;
+
+ AngleInfo(this.gl);
+
+ void update(count, mode, instanceCount) {
+ render["calls"] = render["calls"]! + 1;
+
+ if (mode == WebGL.TRIANGLES) {
+ render["triangles"] = render["triangles"]! + instanceCount * (count / 3.0);
+ } else if (mode == WebGL.LINES) {
+ render["lines"] = render["lines"]! + instanceCount * (count / 2);
+ } else if (mode == WebGL.LINE_STRIP) {
+ render["lines"] = render["lines"]! + instanceCount * (count - 1);
+ } else if (mode == WebGL.LINE_LOOP) {
+ render["lines"] = render["lines"]! + instanceCount * count;
+ } else if (mode == WebGL.POINTS) {
+ render["points"] = render["points"]! + instanceCount * count;
+ } else {
+ console.warning('three.WebGLInfo: Unknown draw mode: $mode ');
+ }
+ }
+
+ void reset() {
+ render["frame"] = render["frame"]! + 1;
+ render["calls"] = 0;
+ render["triangles"] = 0;
+ render["points"] = 0;
+ render["lines"] = 0;
+ }
+
+ void dispose(){}
+}
diff --git a/packages/three_js_angle_renderer/lib/angle/angle_lights.dart b/packages/three_js_angle_renderer/lib/angle/angle_lights.dart
new file mode 100755
index 00000000..3240b303
--- /dev/null
+++ b/packages/three_js_angle_renderer/lib/angle/angle_lights.dart
@@ -0,0 +1,586 @@
+part of three_webgl;
+
+class UniformsCache {
+ UniformsCache();
+
+ Map> lights = {};
+
+ void dispose(){
+ lights.clear();
+ }
+
+ Map get(light) {
+ if (lights[light.id] != null) {
+ return lights[light.id]!;
+ }
+
+ Map? uniforms;
+
+ switch (light.type) {
+ case 'DirectionalLight':
+ uniforms = {"direction": Vector3.zero(), "color": Color(0, 0, 0)};
+ break;
+
+ case 'SpotLight':
+ uniforms = {
+ "position": Vector3.zero(),
+ "direction": Vector3.zero(),
+ "color": Color(0, 0, 0),
+ "distance": 0,
+ "coneCos": 0,
+ "penumbraCos": 0,
+ "decay": 0
+ };
+ break;
+
+ case 'PointLight':
+ uniforms = {"position": Vector3.zero(), "color": Color(1, 1, 1), "distance": 0, "decay": 0};
+ break;
+
+ case 'HemisphereLight':
+ uniforms = {"direction": Vector3.zero(), "skyColor": Color(0, 0, 0), "groundColor": Color(0, 0, 0)};
+ break;
+
+ case 'RectAreaLight':
+ uniforms = {
+ "color": Color(0, 0, 0),
+ "position": Vector3.zero(),
+ "halfWidth": Vector3.zero(),
+ "halfHeight": Vector3.zero()
+ };
+ break;
+ }
+
+ lights[light.id] = uniforms!;
+
+ return uniforms;
+ }
+}
+
+class ShadowUniformsCache {
+ Map> lights = {};
+
+ void dispose(){
+ lights.clear();
+ }
+
+ Map? get(light) {
+ if (lights[light.id] != null) {
+ return lights[light.id];
+ }
+
+ Map uniforms = {};
+
+ switch (light.type) {
+ case 'DirectionalLight':
+ uniforms = {
+ "shadowIntensity": 1,
+ "shadowBias": 0,
+ "shadowNormalBias": 0,
+ "shadowRadius": 1,
+ "shadowMapSize": Vector2.zero()
+ };
+ break;
+
+ case 'SpotLight':
+ uniforms = {
+ 'shadowIntensity': 1,
+ "shadowBias": 0,
+ "shadowNormalBias": 0,
+ "shadowRadius": 1,
+ "shadowMapSize": Vector2.zero()
+ };
+ break;
+
+ case 'PointLight':
+ uniforms = {
+ 'shadowIntensity': 1,
+ "shadowBias": 0,
+ "shadowNormalBias": 0,
+ "shadowRadius": 1,
+ "shadowMapSize": Vector2.zero(),
+ "shadowCameraNear": 1,
+ "shadowCameraFar": 1000
+ };
+ break;
+
+ // TODO (abelnation): set RectAreaLight shadow uniforms
+
+ }
+
+ lights[light.id] = uniforms;
+
+ return uniforms;
+ }
+}
+
+int nextVersion = 0;
+
+int shadowCastingAndTexturingLightsFirst(Light lightA, Light lightB) {
+ return ( lightB.castShadow ? 2 : 0 ) - ( lightA.castShadow ? 2 : 0 ) + ( lightB.map != null? 1 : 0 ) - ( lightA.map != null? 1 : 0 );
+}
+
+class AngleLights {
+ late LightState state;
+ late UniformsCache cache;
+ late ShadowUniformsCache shadowCache;
+ late Vector3 vector3;
+ late Matrix4 matrix4;
+ late Matrix4 matrix42;
+ AngleExtensions extensions;
+
+ AngleLights(this.extensions) {
+ cache = UniformsCache();
+ shadowCache = ShadowUniformsCache();
+
+ state = LightState({
+ "version": 0,
+ "hash": {
+ "directionalLength": -1,
+ "pointLength": -1,
+ "spotLength": -1,
+ "rectAreaLength": -1,
+ "hemiLength": -1,
+
+ "numDirectionalShadows": -1,
+ "numPointShadows": -1,
+ "numSpotShadows": -1,
+ 'numSpotMaps': - 1,
+
+ 'numLightProbes': - 1
+ },
+ "ambient": List.from([0.0, 0.0, 0.0]),
+ "probe": [],
+ "directional": [],
+ "directionalShadow": [],
+ "directionalShadowMap": [],
+ "directionalShadowMatrix": [],
+ "spot": [],
+ 'spotLightMap': [],
+ "spotShadow": [],
+ "spotShadowMap": [],
+ "spotShadowMatrix": [],
+ 'spotLightMatrix': [],
+ "rectArea": [],
+ "rectAreaLTC1": null,
+ "rectAreaLTC2": null,
+ "point": [],
+ "pointShadow": [],
+ "pointShadowMap": [],
+ "pointShadowMatrix": [],
+ "hemi": [],
+ 'numSpotLightShadowsWithMaps': 0,
+ 'numLightProbes': 0
+ });
+
+ for (int i = 0; i < 9; i++) {
+ state.probe.add(Vector3.zero());
+ }
+
+ vector3 = Vector3.zero();
+ matrix4 = Matrix4.identity();
+ matrix42 = Matrix4.identity();
+ }
+
+ void setup(List lights, [bool? physicallyCorrectLights]) {
+ double r = 0.0;
+ double g = 0.0;
+ double b = 0.0;
+
+ for (int i = 0; i < 9; i++) {
+ state.probe[i].setValues(0, 0, 0);
+ }
+
+ int directionalLength = 0;
+ int pointLength = 0;
+ int spotLength = 0;
+ int rectAreaLength = 0;
+ int hemiLength = 0;
+
+ int numDirectionalShadows = 0;
+ int numPointShadows = 0;
+ int numSpotShadows = 0;
+ int numSpotMaps = 0;
+ int numSpotShadowsWithMaps = 0;
+ int numLightProbes = 0;
+
+ lights.sort((a, b) => shadowCastingAndTexturingLightsFirst(a, b));
+
+ // artist-friendly light intensity scaling factor
+ double scaleFactor = (physicallyCorrectLights != true) ? math.pi : 1.0;
+
+ for (int i = 0, l = lights.length; i < l; i++) {
+ final light = lights[i];
+
+ final color = light.color ?? Color();
+ final intensity = light.intensity;
+ final distance = light.distance;
+
+ final shadowMap = (light.shadow != null && light.shadow!.map != null) ? light.shadow!.map!.texture : null;
+
+ if (light is AmbientLight) {
+ r += color.red * intensity;
+ g += color.green * intensity;
+ b += color.blue * intensity;
+ }
+ else if (light is LightProbe) {
+ for (int j = 0; j < 9; j++) {
+ state.probe[j].addScaled(light.sh!.coefficients[j], intensity);
+ }
+ }
+ else if (light is DirectionalLight) {
+ final uniforms = cache.get(light);
+
+ (uniforms["color"] as Color)..setFrom(light.color!)..scale(light.intensity * scaleFactor);
+
+ if (light.castShadow) {
+ final shadow = light.shadow!;
+
+ final shadowUniforms = shadowCache.get(light);
+
+ shadowUniforms?["shadowIntensity"] = shadow.intensity;
+ shadowUniforms?["shadowBias"] = shadow.bias;
+ shadowUniforms?["shadowNormalBias"] = shadow.normalBias;
+ shadowUniforms?["shadowRadius"] = shadow.radius;
+ shadowUniforms?["shadowMapSize"] = shadow.mapSize;
+
+ state.directionalShadow.listSetter(directionalLength, shadowUniforms);
+ state.directionalShadowMap.listSetter(directionalLength, shadowMap);
+ state.directionalShadowMatrix.listSetter( directionalLength, light.shadow!.matrix);
+
+ numDirectionalShadows++;
+ }
+
+ state.directional.listSetter(directionalLength, uniforms);
+
+ directionalLength++;
+ }
+ else if (light is SpotLight) {
+ final uniforms = cache.get(light);
+
+ (uniforms["position"] as Vector3).setFromMatrixPosition(light.matrixWorld);
+ (uniforms["color"] as Color)..setFrom(color)..scale(intensity * scaleFactor);
+
+ uniforms["distance"] = distance;
+
+ uniforms["coneCos"] = math.cos(light.angle!);
+ uniforms["penumbraCos"] = math.cos(light.angle! * (1 - light.penumbra!));
+ uniforms["decay"] = light.decay;
+
+ state.spot.listSetter(spotLength, uniforms);
+ final shadow = light.shadow!;
+
+ if ( light.map != null) {
+ state.spotLightMap.listSetter(numSpotMaps, light.map);
+ numSpotMaps ++;
+
+ // make sure the lightMatrix is up to date
+ // TODO : do it if required only
+ shadow.updateMatrices( light );
+ if (light.castShadow) numSpotShadowsWithMaps ++;
+ }
+
+ state.spotLightMatrix.add(shadow.matrix);
+
+ if (light.castShadow) {
+ final shadowUniforms = shadowCache.get(light);
+
+ shadowUniforms?['shadowIntensity'] = shadow.intensity;
+ shadowUniforms?["shadowBias"] = shadow.bias;
+ shadowUniforms?["shadowNormalBias"] = shadow.normalBias;
+ shadowUniforms?["shadowRadius"] = shadow.radius;
+ shadowUniforms?["shadowMapSize"] = shadow.mapSize;
+
+ state.spotShadow.listSetter(spotLength, shadowUniforms);
+ state.spotShadowMap.listSetter(spotLength, shadowMap);
+ state.spotShadowMatrix.listSetter( spotLength, light.shadow!.matrix);
+
+ numSpotShadows++;
+ }
+
+ spotLength++;
+ }
+ else if (light is RectAreaLight) {
+ final uniforms = cache.get(light);
+
+ // (a) intensity is the total visible light emitted
+ //uniforms.color.copy( color ).scale( intensity / ( light.width * light.height * math.PI ) );
+
+ // (b) intensity is the brightness of the light
+ uniforms["color"]..setFrom(color)..scale(intensity);
+
+ uniforms["halfWidth"].setValues(light.width! * 0.5, 0.0, 0.0);
+ uniforms["halfHeight"].setValues(0.0, light.height! * 0.5, 0.0);
+
+ // state.rectArea[ rectAreaLength ] = uniforms;
+ state.rectArea.listSetter(rectAreaLength, uniforms);
+
+ rectAreaLength++;
+ }
+ else if (light is PointLight) {
+ final uniforms = cache.get(light);
+
+ (uniforms["color"] as Color)..setFrom(light.color!)..scale(light.intensity * scaleFactor);
+ uniforms["distance"] = light.distance ?? 0;
+ uniforms["decay"] = light.decay;
+
+ if (light.castShadow) {
+ final shadow = light.shadow!;
+
+ final shadowUniforms = shadowCache.get(light);
+
+ shadowUniforms?['shadowIntensity'] = shadow.intensity;
+ shadowUniforms?["shadowBias"] = shadow.bias;
+ shadowUniforms?["shadowNormalBias"] = shadow.normalBias;
+ shadowUniforms?["shadowRadius"] = shadow.radius;
+ shadowUniforms?["shadowMapSize"] = shadow.mapSize;
+ shadowUniforms?["shadowCameraNear"] = shadow.camera!.near;
+ shadowUniforms?["shadowCameraFar"] = shadow.camera!.far;
+
+ state.pointShadow.listSetter(pointLength, shadowUniforms);
+ state.pointShadowMap.listSetter(pointLength, shadowMap);
+ state.pointShadowMatrix.listSetter(pointLength, light.shadow!.matrix);
+
+ numPointShadows++;
+ }
+
+ // state.point[ pointLength ] = uniforms;
+ state.point.listSetter(pointLength, uniforms);
+
+ pointLength++;
+ }
+ else if (light is HemisphereLight) {
+ final uniforms = cache.get(light);
+
+ uniforms["skyColor"]..setFrom(light.color)..scale(intensity * scaleFactor);
+ uniforms["groundColor"]..setFrom(light.groundColor)..scale(intensity * scaleFactor);
+
+ // state.hemi[ hemiLength ] = uniforms;
+ state.hemi.listSetter(hemiLength, uniforms);
+
+ hemiLength++;
+ }
+ else {
+ throw (" WebGLLigts type: ${light.type} is not support ..... ");
+ }
+ }
+
+ if (rectAreaLength > 0) {
+ if ( extensions.has( 'OES_texture_float_linear' ) == true ) {
+ state.rectAreaLTC1 = uniformsLib["LTC_FLOAT_1"];
+ state.rectAreaLTC2 = uniformsLib["LTC_FLOAT_2"];
+ }
+ else{
+ state.rectAreaLTC1 = uniformsLib["LTC_HALF_1"];
+ state.rectAreaLTC2 = uniformsLib["LTC_HALF_2"];
+ }
+ }
+
+ state.ambient[0] = r.toDouble();
+ state.ambient[1] = g.toDouble();
+ state.ambient[2] = b.toDouble();
+
+ final hash = state.hash;
+
+ if (hash["directionalLength"] != directionalLength ||
+ hash["pointLength"] != pointLength ||
+ hash["spotLength"] != spotLength ||
+ hash["rectAreaLength"] != rectAreaLength ||
+ hash["hemiLength"] != hemiLength ||
+ hash["numDirectionalShadows"] != numDirectionalShadows ||
+ hash["numPointShadows"] != numPointShadows ||
+ hash["numSpotShadows"] != numSpotShadows ||
+ hash['numSpotMaps'] != numSpotMaps ||
+ hash['numLightProbes'] != numLightProbes ) {
+
+ state.directional.length = directionalLength;
+ state.spot.length = spotLength;
+ state.rectArea.length = rectAreaLength;
+ state.point.length = pointLength;
+ state.hemi.length = hemiLength;
+
+ state.directionalShadow.length = numDirectionalShadows;
+ state.directionalShadowMap.length = numDirectionalShadows;
+ state.pointShadow.length = numPointShadows;
+ state.pointShadowMap.length = numPointShadows;
+ state.spotShadow.length = numSpotShadows;
+ state.spotShadowMap.length = numSpotShadows;
+ state.directionalShadowMatrix.length = numDirectionalShadows;
+ state.pointShadowMatrix.length = numPointShadows;
+ state.spotLightMatrix.length = numSpotShadows + numSpotMaps - numSpotShadowsWithMaps;
+ state.spotLightMap.length = numSpotMaps;
+ state.numSpotLightShadowsWithMaps = numSpotShadowsWithMaps;
+ state.numLightProbes = numLightProbes;
+
+ hash["directionalLength"] = directionalLength;
+ hash["pointLength"] = pointLength;
+ hash["spotLength"] = spotLength;
+ hash["rectAreaLength"] = rectAreaLength;
+ hash["hemiLength"] = hemiLength;
+
+ hash["numDirectionalShadows"] = numDirectionalShadows;
+ hash["numPointShadows"] = numPointShadows;
+ hash["numSpotShadows"] = numSpotShadows;
+
+ hash['numSpotMaps'] = numSpotMaps;
+ hash['numLightProbes'] = numLightProbes;
+
+ state.version = nextVersion++;
+ }
+ }
+
+ void setupView(List lights, Camera camera) {
+ int directionalLength = 0;
+ int pointLength = 0;
+ int spotLength = 0;
+ int rectAreaLength = 0;
+ int hemiLength = 0;
+
+ final viewMatrix = camera.matrixWorldInverse;
+
+ for (int i = 0, l = lights.length; i < l; i++) {
+ final light = lights[i];
+
+ if (light is DirectionalLight) {
+ final uniforms = state.directional[directionalLength];
+
+ uniforms["direction"].setFromMatrixPosition(light.matrixWorld);
+ vector3.setFromMatrixPosition(light.target!.matrixWorld);
+ uniforms["direction"].sub(vector3);
+ uniforms["direction"].transformDirection(viewMatrix);
+
+ directionalLength++;
+ }
+ else if (light is SpotLight) {
+ final uniforms = state.spot[spotLength];
+
+ uniforms["position"].setFromMatrixPosition(light.matrixWorld);
+ uniforms["position"].applyMatrix4(viewMatrix);
+
+ uniforms["direction"].setFromMatrixPosition(light.matrixWorld);
+ vector3.setFromMatrixPosition(light.target!.matrixWorld);
+ uniforms["direction"].sub(vector3);
+ uniforms["direction"].transformDirection(viewMatrix);
+
+ spotLength++;
+ }
+ else if (light is RectAreaLight) {
+ final uniforms = state.rectArea[rectAreaLength];
+
+ uniforms["position"].setFromMatrixPosition(light.matrixWorld);
+ uniforms["position"].applyMatrix4(viewMatrix);
+
+ // extract local rotation of light to derive width/height half vectors
+ matrix42.setFrom(Matrix4.identity());
+ matrix4.setFrom(light.matrixWorld);
+ matrix4.multiply(viewMatrix);
+ matrix42.extractRotation(matrix4);
+
+ uniforms["halfWidth"].setValues(light.width! * 0.5, 0.0, 0.0);
+ uniforms["halfHeight"].setValues(0.0, light.height! * 0.5, 0.0);
+
+ uniforms["halfWidth"].applyMatrix4(matrix42);
+ uniforms["halfHeight"].applyMatrix4(matrix42);
+
+ rectAreaLength++;
+ }
+ else if (light is PointLight) {
+ final uniforms = state.point[pointLength];
+
+ (uniforms["position"] as Vector3).setFromMatrixPosition(light.matrixWorld);
+ uniforms["position"].applyMatrix4(viewMatrix);
+
+ pointLength++;
+ }
+ else if (light is HemisphereLight) {
+ final uniforms = state.hemi[hemiLength];
+
+ uniforms["direction"].setFromMatrixPosition(light.matrixWorld);
+ uniforms["direction"].transformDirection(viewMatrix);
+
+ hemiLength++;
+ }
+ }
+ }
+
+ void dispose(){
+ state.dispose();
+ cache.dispose();
+ shadowCache.dispose();
+ extensions.dispose();
+ }
+}
+
+class LightState {
+ late num version;
+ late Map hash;
+ late List ambient;
+ late List probe;
+ late List directional;
+ late List directionalShadow;
+ late List directionalShadowMap;
+ late List directionalShadowMatrix;
+ late List spot;
+ late List spotLightMap;
+ late List spotShadow;
+ late List spotShadowMap;
+ late List spotShadowMatrix;
+ late List spotLightMatrix;
+ late List rectArea;
+ late List point;
+ late List pointShadow;
+ late List pointShadowMap;
+ late List pointShadowMatrix;
+ late List hemi;
+ late int numLightProbes;
+ late int numSpotLightShadowsWithMaps;
+
+ dynamic rectAreaLTC1;
+ dynamic rectAreaLTC2;
+
+ LightState(Map json) {
+ version = json["version"];
+ hash = json["hash"];
+ ambient = List.from(json["ambient"]);
+ probe = List.from(json["probe"]);
+ directional = json["directional"];
+ directionalShadow = json["directionalShadow"];
+ directionalShadowMap = json["directionalShadowMap"];
+ directionalShadowMatrix = json["directionalShadowMatrix"];
+ spot = json["spot"];
+ spotLightMap = json['spotLightMap'];
+ spotShadow = json["spotShadow"];
+ spotShadowMap = json["spotShadowMap"];
+ spotShadowMatrix = json["spotShadowMatrix"];
+ spotLightMatrix = json["spotLightMatrix"];
+ rectArea = json["rectArea"];
+ point = json["point"];
+ pointShadow = json["pointShadow"];
+ pointShadowMap = json["pointShadowMap"];
+ pointShadowMatrix = json["pointShadowMatrix"];
+ hemi = json["hemi"];
+
+ numSpotLightShadowsWithMaps = json['numSpotLightShadowsWithMaps'];
+ numLightProbes = json['numLightProbes'];
+ }
+
+ void dispose(){
+ hash.clear();
+ ambient.clear();
+ probe.clear();
+ directional.clear();
+ directionalShadow.clear();
+ directionalShadowMap.clear();
+ directionalShadowMatrix.clear();
+ spot.clear();
+ spotShadow.clear();
+ spotShadowMap.clear();
+ spotShadowMatrix.clear();
+ rectArea.clear();
+ point.clear();
+ pointShadow.clear();
+ pointShadowMap.clear();
+ pointShadowMatrix.clear();
+ hemi.clear();
+ }
+}
\ No newline at end of file
diff --git a/packages/three_js_angle_renderer/lib/angle/angle_materials.dart b/packages/three_js_angle_renderer/lib/angle/angle_materials.dart
new file mode 100755
index 00000000..d59fe344
--- /dev/null
+++ b/packages/three_js_angle_renderer/lib/angle/angle_materials.dart
@@ -0,0 +1,386 @@
+part of three_webgl;
+
+class AngleMaterials {
+ bool _didDispose = false;
+ AngleRenderer renderer;
+ AngleProperties properties;
+
+ AngleMaterials(this.renderer, this.properties);
+
+ void dispose(){
+ if(_didDispose) return;
+ _didDispose = true;
+ properties.dispose();
+ }
+
+ void refreshTransformUniform(Texture? map, Map? uniform ) {
+ if(map == null) return;
+ if ( map.matrixAutoUpdate == true) {
+ map.updateMatrix();
+ }
+ uniform?['value']?.setFrom( map.matrix );
+ }
+
+ void refreshFogUniforms(Map uniforms, FogBase fog) {
+ uniforms["fogColor"]["value"].setFrom(fog.color);
+
+ if (fog.isFog) {
+ uniforms["fogNear"]["value"] = fog.near;
+ uniforms["fogFar"]["value"] = fog.far;
+ }
+ else if (fog.isFogExp2) {
+ uniforms["fogDensity"]["value"] = fog.density;
+ }
+ }
+
+ void refreshMaterialUniforms(Map uniforms, Material material, double pixelRatio, double height, RenderTarget? transmissionRenderTarget) {
+ if (material is MeshBasicMaterial) {
+ refreshUniformsCommon(uniforms, material);
+ } else if (material is MeshLambertMaterial) {
+ refreshUniformsCommon(uniforms, material);
+ } else if (material is MeshToonMaterial) {
+ refreshUniformsCommon(uniforms, material);
+ refreshUniformsToon(uniforms, material);
+ } else if (material is MeshPhongMaterial) {
+ refreshUniformsCommon(uniforms, material);
+ refreshUniformsPhong(uniforms, material);
+ } else if (material is MeshPhysicalMaterial) {
+ refreshUniformsCommon(uniforms, material);
+ refreshUniformsStandard(uniforms, material);
+ refreshUniformsPhysical(uniforms, material, transmissionRenderTarget);
+ } else if (material is MeshStandardMaterial) {
+ refreshUniformsCommon(uniforms, material);
+ refreshUniformsStandard(uniforms, material);
+ if ( material is MeshPhysicalMaterial ) {
+ refreshUniformsPhysical( uniforms, material, transmissionRenderTarget );
+ }
+ } else if (material is MeshMatcapMaterial) {
+ refreshUniformsCommon(uniforms, material);
+ refreshUniformsMatcap(uniforms, material);
+ } else if (material is MeshDepthMaterial) {
+ refreshUniformsCommon(uniforms, material);
+ } else if (material is MeshDistanceMaterial) {
+ refreshUniformsCommon(uniforms, material);
+ refreshUniformsDistance(uniforms, material);
+ } else if (material is MeshNormalMaterial) {
+ refreshUniformsCommon(uniforms, material);
+ } else if (material is LineBasicMaterial) {
+ refreshUniformsLine(uniforms, material);
+ }else if (material is LineDashedMaterial) {
+ refreshUniformsLine(uniforms, material);
+ refreshUniformsDash(uniforms, material);
+ }else if (material is PointsMaterial) {
+ refreshUniformsPoints(uniforms, material, pixelRatio, height);
+ } else if (material is SpriteMaterial) {
+ refreshUniformsSprites(uniforms, material);
+ } else if (material is ShadowMaterial) {
+ uniforms["color"]["value"].setFrom(material.color);
+ uniforms["opacity"]["value"] = material.opacity;
+ } else if (material is ShaderMaterial) {
+ material.uniformsNeedUpdate = false; // #15581
+ }
+ }
+
+ void refreshUniformsCommon(Map uniforms, Material material) {
+ uniforms["opacity"]["value"] = material.opacity;
+
+ uniforms["diffuse"]["value"].setFrom(material.color);
+
+ if (material.emissive != null) {
+ uniforms["emissive"]["value"].setFrom(material.emissive)?.scale(material.emissiveIntensity);
+ }
+
+ if (material.map != null) {
+ uniforms["map"]["value"] = material.map;
+ refreshTransformUniform( material.map, uniforms['mapTransform'] );
+ }
+
+ if (material.alphaMap != null) {
+ uniforms["alphaMap"]["value"] = material.alphaMap;
+ refreshTransformUniform( material.alphaMap!, uniforms['alphaMapTransform'] );
+ }
+
+ if (material.bumpMap != null) {
+ uniforms["bumpMap"]["value"] = material.bumpMap;
+ refreshTransformUniform( material.bumpMap!, uniforms['bumpMapTransform'] );
+ uniforms["bumpScale"]["value"] = material.bumpScale;
+ if (material.side == BackSide) uniforms["bumpScale"]["value"] *= -1;
+ }
+
+ if (material.displacementMap != null) {
+ uniforms["displacementMap"]["value"] = material.displacementMap;
+ refreshTransformUniform( material.displacementMap!, uniforms['displacementMapTransform'] );
+ uniforms["displacementScale"]["value"] = material.displacementScale;
+ uniforms["displacementBias"]["value"] = material.displacementBias;
+ }
+
+ if (material.emissiveMap != null) {
+ uniforms["emissiveMap"]["value"] = material.emissiveMap;
+ refreshTransformUniform( material.emissiveMap!, uniforms['emissiveMapTransform'] );
+ }
+
+ if (material.normalMap != null) {
+ uniforms["normalMap"]["value"] = material.normalMap;
+ refreshTransformUniform( material.normalMap!, uniforms['normalMapTransform'] );
+ uniforms["normalScale"]["value"].setFrom(material.normalScale);
+ if (material.side == BackSide) uniforms["normalScale"]["value"].negate();
+ }
+
+ if (material.specularMap != null) {
+ uniforms["specularMap"]["value"] = material.specularMap;
+ refreshTransformUniform( material.specularMap!, uniforms['specularMapTransform'] );
+ }
+
+ if (material.alphaTest > 0) {
+ uniforms["alphaTest"]["value"] = material.alphaTest;
+ }
+
+ final materialProperties = properties.get( material );
+
+ final envMap = materialProperties['envMap'];
+ final envMapRotation = materialProperties['envMapRotation'] ?? Euler();
+
+ if (envMap != null) {
+ uniforms["envMap"]["value"] = envMap;
+ _e1.copy( envMapRotation );
+
+ // accommodate left-handed frame
+ _e1.x *= - 1; _e1.y *= - 1; _e1.z *= - 1;
+
+ if ( envMap is CubeTexture && envMap is! Angle3DRenderTarget) {
+ // environment maps which are not cube render targets or PMREMs follow a different convention
+ _e1.y *= - 1;
+ _e1.z *= - 1;
+ }
+
+ uniforms['envMapRotation']?['value'].setFromMatrix4( _m1.makeRotationFromEuler( _e1 ) );
+
+ uniforms["flipEnvMap"]?["value"] = (envMap is CubeTexture && envMap is! Angle3DRenderTarget) ? -1 : 1;
+ uniforms["reflectivity"]["value"] = material.reflectivity;
+ uniforms["ior"]["value"] = material.ior;
+ uniforms["refractionRatio"]["value"] = material.refractionRatio;
+ }
+
+ if (material.lightMap != null) {
+ uniforms["lightMap"]["value"] = material.lightMap;
+ uniforms["lightMapIntensity"]["value"] = material.lightMapIntensity!;
+ refreshTransformUniform( material.lightMap!, uniforms['lightMapTransform'] );
+ }
+
+ if (material.aoMap != null) {
+ uniforms["aoMap"]["value"] = material.aoMap;
+ uniforms["aoMapIntensity"]["value"] = material.aoMapIntensity;
+ refreshTransformUniform( material.aoMap!, uniforms['aoMapTransform'] );
+ }
+ }
+
+ void refreshUniformsLine(Map uniforms, Material material) {
+ uniforms["diffuse"]["value"].setFrom(material.color);
+ uniforms["opacity"]["value"] = material.opacity;
+
+ if (material.map != null) {
+ uniforms['map']['value'] = material.map;
+ refreshTransformUniform( material.map, uniforms['mapTransform'] );
+ }
+ }
+
+ void refreshUniformsDash(Map uniforms, Material material) {
+ uniforms["dashSize"]["value"] = material.dashSize;
+ uniforms["totalSize"]["value"] = (material.dashSize ?? 0) + (material.gapSize ?? 0);
+ uniforms["scale"]["value"] = material.scale;
+ }
+
+ void refreshUniformsPoints(Map uniforms, Material material, double pixelRatio, double height) {
+ uniforms["diffuse"]["value"].setFrom(material.color);
+ uniforms["opacity"]["value"] = material.opacity;
+ uniforms["size"]["value"] = material.size! * pixelRatio;
+ uniforms["scale"]["value"] = height * 0.5;
+
+ if (material.map != null) {
+ uniforms["map"]["value"] = material.map;
+ refreshTransformUniform( material.map, uniforms['uvTransform'] );
+ }
+
+ if (material.alphaMap != null) {
+ uniforms["alphaMap"]["value"] = material.alphaMap;
+ refreshTransformUniform( material.alphaMap!, uniforms['alphaMapTransform'] );
+ }
+
+ if (material.alphaTest > 0) {
+ uniforms["alphaTest"]["value"] = material.alphaTest;
+ }
+ }
+
+ void refreshUniformsSprites(Map uniforms, Material material) {
+ uniforms["diffuse"]["value"].setFrom(material.color);
+ uniforms["opacity"]["value"] = material.opacity;
+ uniforms["rotation"]["value"] = material.rotation;
+
+ if (material.map != null) {
+ uniforms["map"]["value"] = material.map;
+ refreshTransformUniform( material.map, uniforms['mapTransform'] );
+ }
+
+ if (material.alphaMap != null) {
+ uniforms["alphaMap"]["value"] = material.alphaMap;
+ refreshTransformUniform( material.alphaMap!, uniforms['alphaMapTransform'] );
+ }
+
+ if (material.alphaTest > 0) {
+ uniforms["alphaTest"]["value"] = material.alphaTest;
+ }
+ }
+
+ void refreshUniformsPhong(Map uniforms, Material material) {
+ uniforms["specular"]["value"].setFrom(material.specular);
+ uniforms["shininess"]["value"] = math.max(material.shininess!, 1e-4); // to prevent pow( 0.0, 0.0 )
+ }
+
+ void refreshUniformsToon(Map uniforms, Material material) {
+ if (material.gradientMap != null) {
+ uniforms["gradientMap"]["value"] = material.gradientMap;
+ }
+ }
+
+ void refreshUniformsStandard(Map uniforms, Material material) {
+ uniforms["metalness"]["value"] = material.metalness;
+ if (material.metalnessMap != null) {
+ uniforms["metalnessMap"]["value"] = material.metalnessMap;
+ refreshTransformUniform( material.metalnessMap, uniforms['metalnessMapTransform'] );
+ }
+
+ uniforms["roughness"]["value"] = material.roughness;
+ if (material.roughnessMap != null) {
+ uniforms["roughnessMap"]["value"] = material.roughnessMap;
+ refreshTransformUniform( material.roughnessMap, uniforms['roughnessMapTransform'] );
+ }
+
+ if (material.envMap != null) {
+ //uniforms.envMap.value = material.envMap; // part of uniforms common
+ uniforms["envMapIntensity"]["value"] = material.envMapIntensity;
+ }
+ }
+
+ void refreshUniformsPhysical(Map uniforms,Material material, RenderTarget? transmissionRenderTarget) {
+ uniforms["ior"]["value"] = material.ior; // also part of uniforms common
+
+ if (material.sheen > 0) {
+ uniforms["sheenColor"]["value"].setFrom(material.sheenColor).multiplyScalar( material.sheen );
+
+ uniforms["sheenRoughness"]["value"] = material.sheenRoughness;
+
+ if (material.sheenColorMap != null) {
+ uniforms["sheenColorMap"]["value"] = material.sheenColorMap;
+ refreshTransformUniform( material.sheenColorMap!, uniforms['sheenColorMapTransform'] );
+ }
+
+ if (material.sheenRoughnessMap != null) {
+ uniforms["sheenRoughnessMap"]["value"] = material.sheenRoughnessMap;
+ refreshTransformUniform( material.sheenRoughnessMap!, uniforms['sheenRoughnessMapTransform'] );
+ }
+ }
+
+ if (material.clearcoat > 0) {
+ uniforms["clearcoat"]["value"] = material.clearcoat;
+ uniforms["clearcoatRoughness"]["value"] = material.clearcoatRoughness;
+
+ if (material.clearcoatMap != null) {
+ uniforms["clearcoatMap"]["value"] = material.clearcoatMap;
+ refreshTransformUniform( material.clearcoatMap!, uniforms['clearcoatMapTransform'] );
+ }
+
+ if (material.clearcoatRoughnessMap != null) {
+ uniforms["clearcoatRoughnessMap"]["value"] = material.clearcoatRoughnessMap;
+ refreshTransformUniform( material.clearcoatRoughnessMap!, uniforms['clearcoatRoughnessMapTransform'] );
+ }
+
+ if (material.clearcoatNormalMap != null) {
+ uniforms["clearcoatNormalMap"]["value"] = material.clearcoatNormalMap;
+ refreshTransformUniform( material.clearcoatNormalMap!, uniforms['clearcoatNormalMapTransform'] );
+ uniforms["clearcoatNormalScale"]["value"].setFrom(material.clearcoatNormalScale);
+ if (material.side == BackSide) {
+ uniforms["clearcoatNormalScale"]["value"].negate();
+ }
+ }
+ }
+
+ if (material is MeshPhysicalMaterial && material.dispersion > 0 ) {
+ uniforms['dispersion']['value'] = material.dispersion;
+ }
+
+ if (material is MeshPhysicalMaterial && material.iridescence > 0 ) {
+ uniforms['iridescence']['value'] = material.iridescence;
+ uniforms['iridescenceIOR']['value'] = material.iridescenceIOR;
+ uniforms['iridescenceThicknessMinimum']['value'] = material.iridescenceThicknessRange[ 0 ];
+ uniforms['iridescenceThicknessMaximum']['value'] = material.iridescenceThicknessRange[ 1 ];
+
+ if ( material.iridescenceMap != null) {
+ uniforms['iridescenceMap']['value'] = material.iridescenceMap;
+ refreshTransformUniform( material.iridescenceMap!, uniforms['iridescenceMapTransform'] );
+ }
+
+ if ( material.iridescenceThicknessMap != null) {
+ uniforms['iridescenceThicknessMap']['value'] = material.iridescenceThicknessMap;
+ refreshTransformUniform( material.iridescenceThicknessMap!, uniforms['iridescenceThicknessMapTransform'] );
+ }
+ }
+
+ if (material.transmission > 0) {
+ uniforms["transmission"]["value"] = material.transmission;
+ uniforms["transmissionSamplerMap"]["value"] = transmissionRenderTarget?.texture;
+ uniforms["transmissionSamplerSize"]["value"].setValues(transmissionRenderTarget?.width.toDouble() ?? 0.0, transmissionRenderTarget?.height.toDouble() ?? 0.0);
+
+ if (material.transmissionMap != null) {
+ uniforms["transmissionMap"]["value"] = material.transmissionMap;
+ refreshTransformUniform( material.transmissionMap, uniforms['transmissionMapTransform'] );
+ }
+
+ uniforms["thickness"]["value"] = material.thickness;
+
+ if (material.thicknessMap != null) {
+ uniforms["thicknessMap"]["value"] = material.thicknessMap;
+ refreshTransformUniform( material.thicknessMap!, uniforms['thicknessMapTransform'] );
+ }
+
+ uniforms["attenuationDistance"]["value"] = material.attenuationDistance;
+ uniforms["attenuationColor"]["value"].setFrom(material.attenuationColor);
+ }
+
+ if (material is MeshPhysicalMaterial && material.anisotropy > 0 ) {
+ uniforms['anisotropyVector']['value'].setValues( material.anisotropy * math.cos( material.anisotropyRotation ), material.anisotropy * math.sin( material.anisotropyRotation ) );
+ if ( material.anisotropyMap != null) {
+ uniforms['anisotropyMap']['value'] = material.anisotropyMap;
+ refreshTransformUniform( material.anisotropyMap!, uniforms['anisotropyMapTransform'] );
+ }
+ }
+
+ uniforms["specularIntensity"]["value"] = material.specularIntensity;
+ uniforms["attenuationColor"]["value"].setFrom(material.attenuationColor);
+ if (material.specularColorMap != null) {
+ uniforms["specularColorMap"]["value"] = material.specularColorMap;
+ refreshTransformUniform( material.specularColorMap!, uniforms['specularColorMapTransform'] );
+ }
+ if (material.specularIntensityMap != null) {
+ uniforms["specularIntensityMap"]["value"] = material.specularIntensityMap;
+ refreshTransformUniform( material.specularIntensityMap!, uniforms['specularIntensityMapTransform'] );
+ }
+ }
+
+ void refreshUniformsMatcap(Map uniforms, Material material) {
+ if (material.matcap != null) {
+ uniforms["matcap"]["value"] = material.matcap;
+ }
+ }
+
+ void refreshUniformsDistance(Map uniforms, MeshDistanceMaterial material) {
+ final light = properties.get( material )['light'];
+
+ uniforms['referencePosition']['value'].setFromMatrixPosition( light.matrixWorld );
+ uniforms['nearDistance']['value'] = light.shadow.camera.near;
+ uniforms['farDistance']['value'] = light.shadow.camera.far;
+
+ // uniforms["referencePosition"]["value"].setFrom(material.referencePosition);
+ // uniforms["nearDistance"]["value"] = material.nearDistance;
+ // uniforms["farDistance"]["value"] = material.farDistance;
+ }
+}
diff --git a/packages/three_js_angle_renderer/lib/angle/angle_morphtargets.dart b/packages/three_js_angle_renderer/lib/angle/angle_morphtargets.dart
new file mode 100755
index 00000000..9483bbc1
--- /dev/null
+++ b/packages/three_js_angle_renderer/lib/angle/angle_morphtargets.dart
@@ -0,0 +1,170 @@
+part of three_webgl;
+
+int numericalSort(a, b) {
+ return a[0] - b[0];
+}
+
+int absNumericalSort(a, b) {
+ return b[1].abs() >= a[1].abs() ? 1 : -1;
+}
+
+void denormalize(Vector morph, BufferAttribute attribute) {
+ double denominator = 1;
+ TypedDataList array = attribute is InterleavedBufferAttribute ? attribute.data!.array : attribute.array;
+
+ if (array is Int8List) {
+ denominator = 127;
+ }
+ else if (array is Int16List) {
+ denominator = 32767;
+ }
+ else if (array is Int32List) {
+ denominator = 2147483647;
+ }
+ else {
+ console.error('three.WebGLMorphtargets: Unsupported morph attribute data type: $array');
+ }
+
+ morph.divideScalar(denominator);
+}
+
+class AngleMorphtargets {
+ bool _didDispose = false;
+ final influencesList = {};
+ final morphInfluences = Float32List(8);
+ final morphTextures = WeakMap();
+ final morph = Vector4.zero();
+
+ List> workInfluences = [];
+
+ RenderingContext gl;
+ AngleCapabilities capabilities;
+ AngleTextures textures;
+
+ AngleMorphtargets(this.gl, this.capabilities, this.textures) {
+ for (int i = 0; i < 8; i++) {
+ workInfluences.add([i, 0]);
+ }
+ }
+
+ void dispose(){
+ if(_didDispose) return;
+ _didDispose = true;
+ influencesList.clear();
+ morphTextures.dispose();
+ workInfluences.clear();
+ capabilities.dispose();
+ textures.dispose();
+ }
+
+ void update(Object3D object, BufferGeometry geometry, AngleProgram program) {
+ Float32List? objectInfluences = Float32List.fromList(object.morphTargetInfluences);
+
+ final morphAttribute = geometry.morphAttributes["position"] ?? geometry.morphAttributes["normal"] ?? geometry.morphAttributes["color"];
+ final morphTargetsCount = (morphAttribute != null) ? morphAttribute.length : 0;
+
+ Map? entry = morphTextures.get(geometry);
+
+ if (entry == null || (entry["count"] != morphTargetsCount)) {
+ if (entry != null) entry["texture"].dispose();
+
+ final hasMorphPosition = geometry.morphAttributes["position"] != null;
+ final hasMorphNormals = geometry.morphAttributes["normal"] != null;
+ final hasMorphColors = geometry.morphAttributes["color"] != null;
+
+ final morphTargets = geometry.morphAttributes["position"] ?? [];
+ final morphNormals = geometry.morphAttributes["normal"] ?? [];
+ final morphColors = geometry.morphAttributes["color"] ?? [];
+
+ int vertexDataCount = 0;
+ if (hasMorphPosition) vertexDataCount = 1;
+ if (hasMorphNormals) vertexDataCount = 2;
+ if (hasMorphColors) vertexDataCount = 3;
+
+ int width = (geometry.attributes["position"].count * vertexDataCount).toInt();
+ int height = 1;
+
+ if (width > capabilities.maxTextureSize) {
+ height = (width / capabilities.maxTextureSize).ceil();
+ width = capabilities.maxTextureSize.toInt();
+ }
+
+ final buffer = Float32List((width * height * 4 * morphTargetsCount).toInt());
+
+ final texture = DataArrayTexture(buffer, width, height, morphTargetsCount);
+ texture.type = FloatType;
+ texture.needsUpdate = true;
+
+ int vertexDataStride = vertexDataCount * 4;
+
+ for (int i = 0; i < morphTargetsCount; i++) {
+ final morphTarget = morphTargets[i];
+
+ int offset = (width * height * 4 * i).toInt();
+
+ for (int j = 0; j < morphTarget.count; j++) {
+ final stride = j * vertexDataStride;
+
+ if (hasMorphPosition) {
+ morph.fromBuffer(morphTarget, j);
+
+ buffer[offset + stride + 0] = morph.x;
+ buffer[offset + stride + 1] = morph.y;
+ buffer[offset + stride + 2] = morph.z;
+ buffer[offset + stride + 3] = 0;
+ }
+
+ if (hasMorphNormals) {
+ final morphNormal = morphNormals[i];
+ morph.fromBuffer(morphNormal, j);
+
+ buffer[offset + stride + 4] = morph.x;
+ buffer[offset + stride + 5] = morph.y;
+ buffer[offset + stride + 6] = morph.z;
+ buffer[offset + stride + 7] = 0;
+ }
+
+ if (hasMorphColors) {
+ final morphColor = morphColors[i];
+ morph.fromBuffer(morphColor, j);
+
+ buffer[offset + stride + 8] = morph.x;
+ buffer[offset + stride + 9] = morph.y;
+ buffer[offset + stride + 10] = morph.z;
+ buffer[offset + stride + 11] = ((morphColor.itemSize == 4) ? morph.w : 1);
+ }
+ }
+ }
+
+ entry = {"count": morphTargetsCount, "texture": texture, "size": Vector2(width.toDouble(), height.toDouble())};
+
+ morphTextures.set(geometry, entry);
+
+ void disposeTexture(event) {
+ texture.dispose();
+ morphTextures.delete(geometry);
+ geometry.removeEventListener('dispose', disposeTexture);
+ }
+
+ geometry.addEventListener('dispose', disposeTexture);
+ }
+
+ if ( object is InstancedMesh && object.morphTexture != null ) {
+ program.getUniforms().setValue( gl, 'morphTexture', object.morphTexture, textures );
+ }
+ else {
+ double morphInfluencesSum = 0;
+
+ for (int i = 0; i < objectInfluences.length; i++) {
+ morphInfluencesSum += objectInfluences[i];
+ }
+
+ final morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum;
+ program.getUniforms().setValue(gl, 'morphTargetBaseInfluence', morphBaseInfluence);
+ program.getUniforms().setValue(gl, 'morphTargetInfluences', objectInfluences);
+ }
+
+ program.getUniforms().setValue(gl, 'morphTargetsTexture', entry["texture"], textures);
+ program.getUniforms().setValue(gl, 'morphTargetsTextureSize', entry["size"]);
+ }
+}
diff --git a/packages/three_js_angle_renderer/lib/angle/angle_objects.dart b/packages/three_js_angle_renderer/lib/angle/angle_objects.dart
new file mode 100755
index 00000000..63016a1f
--- /dev/null
+++ b/packages/three_js_angle_renderer/lib/angle/angle_objects.dart
@@ -0,0 +1,76 @@
+part of three_webgl;
+
+class AngleObjects {
+ bool _didDispose = false;
+ final updateMap = WeakMap();
+ AngleInfo info;
+ RenderingContext gl;
+ AngleGeometries geometries;
+ AngleAttributes attributes;
+
+ AngleObjects(this.gl, this.geometries, this.attributes, this.info);
+
+ BufferGeometry update(Object3D object) {
+ num frame = info.render["frame"]!;
+
+ final geometry = object.geometry;
+
+ final buffergeometry = geometries.get(geometry!);
+
+ // Update once per frame
+
+ if (updateMap.get(buffergeometry) != frame) {
+ geometries.update(buffergeometry);
+ updateMap.add(key: buffergeometry, value: frame);
+ }
+
+ // print(" WebGLObjects update object: ${object} ${object.type} ");
+
+ if (object is InstancedMesh) {
+ if (object.hasEventListener('dispose', onInstancedMeshDispose) == false) {
+ object.addEventListener('dispose', onInstancedMeshDispose);
+ }
+
+ if ( updateMap.get( object ) != frame ) {
+ // print(" WebGLObjects update 2 object: ${object} ${object.instanceMatrix} ");
+ attributes.update(object.instanceMatrix, WebGL.ARRAY_BUFFER);
+
+ if (object.instanceColor != null) {
+ attributes.update(object.instanceColor, WebGL.ARRAY_BUFFER);
+ }
+ updateMap.set( object, frame );
+ }
+ }
+
+ if ( object is SkinnedMesh ) {
+ final skeleton = object.skeleton;
+
+ if ( updateMap.get( skeleton ) != frame ) {
+ skeleton?.update();
+ updateMap.set( skeleton, frame );
+ }
+ }
+
+ return buffergeometry;
+ }
+
+ void dispose() {
+ if(_didDispose) return;
+ _didDispose = true;
+ updateMap.clear();
+ attributes.dispose();
+ geometries.dispose();
+ info.dispose();
+ }
+ void onInstancedMeshDispose(event) {
+ final instancedMesh = event.target;
+
+ instancedMesh.removeEventListener('dispose', onInstancedMeshDispose);
+
+ attributes.remove(instancedMesh.instanceMatrix);
+
+ if (instancedMesh.instanceColor != null) {
+ attributes.remove(instancedMesh.instanceColor);
+ }
+ }
+}
diff --git a/packages/three_js_angle_renderer/lib/angle/angle_program.dart b/packages/three_js_angle_renderer/lib/angle/angle_program.dart
new file mode 100755
index 00000000..72a631ce
--- /dev/null
+++ b/packages/three_js_angle_renderer/lib/angle/angle_program.dart
@@ -0,0 +1,521 @@
+part of three_webgl;
+
+int programIdCount = 0;
+const COMPLETION_STATUS_KHR = 0x91B1;
+
+class DefaultProgram {
+ int id = -1;
+}
+
+class AngleProgram extends DefaultProgram with AngleProgramExtra {
+ bool _didDispose = false;
+ late String name;
+ AngleRenderer renderer;
+ String cacheKey;
+ AngleBindingStates bindingStates;
+ int usedTimes = 1;
+ late RenderingContext gl;
+ Parameters parameters;
+ late Program? program;
+
+ late String vertexShader;
+ late String fragmentShader;
+ late Map diagnostics;
+
+ AngleUniforms? cachedUniforms;
+ Map? cachedAttributes;
+
+ late Function(AngleProgram) onFirstUse;
+
+ AngleProgram(this.renderer, this.cacheKey, this.parameters, this.bindingStates) {
+ name = parameters.shaderName;
+ id = programIdCount++;
+
+ gl = renderer.getContext();
+ program = gl.createProgram();
+ init();
+ }
+
+ void dispose(){
+ if(_didDispose) return;
+ _didDispose = true;
+ renderer.dispose();
+ bindingStates.dispose();
+ parameters.dispose();
+ diagnostics.clear();
+ cachedUniforms?.dispose();
+ cachedAttributes?.clear();
+ }
+
+ void init() {
+ final defines = parameters.defines;
+
+ vertexShader = parameters.vertexShader;
+ fragmentShader = parameters.fragmentShader;
+
+ final shadowMapTypeDefine = generateShadowMapTypeDefine(parameters);
+ final envMapTypeDefine = generateEnvMapTypeDefine(parameters);
+ final envMapModeDefine = generateEnvMapModeDefine(parameters);
+ final envMapBlendingDefine = generateEnvMapBlendingDefine(parameters);
+
+ final envMapCubeUVSize = generateCubeUVSize(parameters);
+ final customVertexExtensions = generateVertexExtensions( parameters );
+
+ final customDefines = generateDefines( defines );
+
+ program ??= gl.createProgram();
+
+ String prefixVertex, prefixFragment;
+ String versionString = parameters.glslVersion != null ? '#version ${parameters.glslVersion}\n' : '';
+
+ if (parameters.isRawShaderMaterial) {
+ prefixVertex = [
+ '#define SHADER_TYPE ${parameters.shaderType}',
+ '#define SHADER_NAME ${parameters.shaderName}',
+ customDefines
+ ].where((s) => filterEmptyLine(s)).join('\n');
+
+ if (prefixVertex.isNotEmpty) {
+ prefixVertex += "\n";
+ }
+
+ prefixFragment = [
+ '#define SHADER_TYPE ${parameters.shaderType}',
+ '#define SHADER_NAME ${parameters.shaderName}',
+ customDefines
+ ].where((s) => filterEmptyLine(s)).join('\n');
+
+ if (prefixFragment.isNotEmpty) {
+ prefixFragment += "\n";
+ }
+ }
+ else {
+ prefixVertex = [
+ generatePrecision(parameters),
+ '#define SHADER_TYPE ${parameters.shaderType}',
+ '#define SHADER_NAME ${parameters.shaderName}',
+
+ customDefines,
+
+ parameters.extensionClipCullDistance ? '#define USE_CLIP_DISTANCE' : '',
+ parameters.batching ? '#define USE_BATCHING' : '',
+ parameters.instancing ? '#define USE_INSTANCING' : '',
+ parameters.instancingColor ? '#define USE_INSTANCING_COLOR' : '',
+ parameters.instancingMorph ? '#define USE_INSTANCING_MORPH' : '',
+
+ parameters.useFog && parameters.fog ? '#define USE_FOG' : '',
+ parameters.useFog && parameters.fogExp2 ? '#define FOG_EXP2' : '',
+
+ parameters.map ? '#define USE_MAP' : '',
+ parameters.envMap ? '#define USE_ENVMAP' : '',
+ parameters.envMap ? '#define $envMapModeDefine' : '',
+ parameters.lightMap ? '#define USE_LIGHTMAP' : '',
+ parameters.aoMap ? '#define USE_AOMAP' : '',
+ parameters.bumpMap ? '#define USE_BUMPMAP' : '',
+ parameters.normalMap ? '#define USE_NORMALMAP' : '',
+ parameters.normalMapObjectSpace ? '#define USE_NORMALMAP_OBJECTSPACE' : '',
+ parameters.normalMapTangentSpace ? '#define USE_NORMALMAP_TANGENTSPACE' : '',
+ parameters.displacementMap ? '#define USE_DISPLACEMENTMAP' : '',
+ parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '',
+
+ parameters.anisotropy ? '#define USE_ANISOTROPY' : '',
+ parameters.anisotropyMap ? '#define USE_ANISOTROPYMAP' : '',
+
+ parameters.clearcoatMap ? '#define USE_CLEARCOATMAP' : '',
+ parameters.clearcoatRoughnessMap ? '#define USE_CLEARCOAT_ROUGHNESSMAP' : '',
+ parameters.clearcoatNormalMap ? '#define USE_CLEARCOAT_NORMALMAP' : '',
+
+ parameters.iridescenceMap ? '#define USE_IRIDESCENCEMAP' : '',
+ parameters.iridescenceThicknessMap ? '#define USE_IRIDESCENCE_THICKNESSMAP' : '',
+
+ parameters.specularMap ? '#define USE_SPECULARMAP' : '',
+ parameters.specularColorMap ? '#define USE_SPECULAR_COLORMAP' : '',
+ parameters.specularIntensityMap ? '#define USE_SPECULAR_INTENSITYMAP' : '',
+
+ parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '',
+ parameters.metalnessMap ? '#define USE_METALNESSMAP' : '',
+ parameters.alphaMap ? '#define USE_ALPHAMAP' : '',
+ parameters.alphaHash ? '#define USE_ALPHAHASH' : '',
+
+ parameters.transmission ? '#define USE_TRANSMISSION' : '',
+ parameters.transmissionMap ? '#define USE_TRANSMISSIONMAP' : '',
+ parameters.thicknessMap ? '#define USE_THICKNESSMAP' : '',
+
+ parameters.sheenColorMap ? '#define USE_SHEEN_COLORMAP' : '',
+ parameters.sheenRoughnessMap ? '#define USE_SHEEN_ROUGHNESSMAP' : '',
+
+ parameters.mapUv != null? '#define MAP_UV ${parameters.mapUv!}': '',
+ parameters.alphaMapUv != null? '#define ALPHAMAP_UV ${parameters.alphaMapUv!}': '',
+ parameters.lightMapUv != null? '#define LIGHTMAP_UV ${parameters.lightMapUv!}': '',
+ parameters.aoMapUv != null? '#define AOMAP_UV ${parameters.aoMapUv!}' : '',
+ parameters.emissiveMapUv != null? '#define EMISSIVEMAP_UV ${parameters.emissiveMapUv!}' : '',
+ parameters.bumpMapUv != null? '#define BUMPMAP_UV ${parameters.bumpMapUv!}' : '',
+ parameters.normalMapUv != null? '#define NORMALMAP_UV ${parameters.normalMapUv!}': '',
+ parameters.displacementMapUv != null? '#define DISPLACEMENTMAP_UV ${parameters.displacementMapUv!}': '',
+
+ parameters.metalnessMapUv != null? '#define METALNESSMAP_UV ${parameters.metalnessMapUv!}': '',
+ parameters.roughnessMapUv != null? '#define ROUGHNESSMAP_UV ${parameters.roughnessMapUv!}': '',
+
+ parameters.anisotropyMapUv != null? '#define ANISOTROPYMAP_UV ${parameters.anisotropyMapUv!}': '',
+
+ parameters.clearcoatMapUv != null? '#define CLEARCOATMAP_UV ${parameters.clearcoatMapUv!}': '',
+ parameters.clearcoatNormalMapUv != null? '#define CLEARCOAT_NORMALMAP_UV ${parameters.clearcoatNormalMapUv!}': '',
+ parameters.clearcoatRoughnessMapUv != null? '#define CLEARCOAT_ROUGHNESSMAP_UV ${parameters.clearcoatRoughnessMapUv!}' : '',
+
+ parameters.iridescenceMapUv != null? '#define IRIDESCENCEMAP_UV ${parameters.iridescenceMapUv!}': '',
+ parameters.iridescenceThicknessMapUv != null? '#define IRIDESCENCE_THICKNESSMAP_UV ${parameters.iridescenceThicknessMapUv!}': '',
+
+ parameters.sheenColorMapUv != null? '#define SHEEN_COLORMAP_UV ${parameters.sheenColorMapUv!}': '',
+ parameters.sheenRoughnessMapUv != null? '#define SHEEN_ROUGHNESSMAP_UV ${parameters.sheenRoughnessMapUv!}': '',
+
+ parameters.specularMapUv != null? '#define SPECULARMAP_UV ${parameters.specularMapUv!}': '',
+ parameters.specularColorMapUv != null? '#define SPECULAR_COLORMAP_UV ${parameters.specularColorMapUv!}': '',
+ parameters.specularIntensityMapUv != null? '#define SPECULAR_INTENSITYMAP_UV ${parameters.specularIntensityMapUv!}' : '',
+
+ parameters.transmissionMapUv != null? '#define TRANSMISSIONMAP_UV ${parameters.transmissionMapUv!}': '',
+ parameters.thicknessMapUv != null? '#define THICKNESSMAP_UV ${parameters.thicknessMapUv!}' : '',
+
+ //
+
+ parameters.vertexTangents && !parameters.flatShading? '#define USE_TANGENT' : '',
+ parameters.vertexColors ? '#define USE_COLOR' : '',
+ parameters.vertexAlphas ? '#define USE_COLOR_ALPHA' : '',
+ parameters.vertexUv1s ? '#define USE_UV1' : '',
+ parameters.vertexUv2s ? '#define USE_UV2' : '',
+ parameters.vertexUv3s ? '#define USE_UV3' : '',
+
+ parameters.pointsUvs ? '#define USE_POINTS_UV' : '',
+
+ parameters.flatShading ? '#define FLAT_SHADED' : '',
+
+ parameters.skinning ? '#define USE_SKINNING' : '',
+
+ parameters.morphTargets ? '#define USE_MORPHTARGETS' : '',
+ parameters.morphNormals && !parameters.flatShading? '#define USE_MORPHNORMALS' : '',
+ ( parameters.morphColors ) ? '#define USE_MORPHCOLORS' : '',
+ ( parameters.morphTargetsCount > 0 ) ? '#define MORPHTARGETS_TEXTURE' : '',
+ ( parameters.morphTargetsCount > 0 ) ? '#define MORPHTARGETS_TEXTURE_STRIDE ${parameters.morphTextureStride}' : '',
+ ( parameters.morphTargetsCount > 0 ) ? '#define MORPHTARGETS_COUNT ${parameters.morphTargetsCount}': '',
+ parameters.doubleSided ? '#define DOUBLE_SIDED' : '',
+ parameters.flipSided ? '#define FLIP_SIDED' : '',
+
+ parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '',
+ parameters.shadowMapEnabled ? '#define $shadowMapTypeDefine': '',
+
+ parameters.sizeAttenuation ? '#define USE_SIZEATTENUATION' : '',
+
+ parameters.numLightProbes > 0 ? '#define USE_LIGHT_PROBES' : '',
+
+ parameters.useLegacyLights ? '#define LEGACY_LIGHTS' : '',
+
+ parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '',
+
+ 'uniform mat4 modelMatrix;',
+ 'uniform mat4 modelViewMatrix;',
+ 'uniform mat4 projectionMatrix;',
+ 'uniform mat4 viewMatrix;',
+ 'uniform mat3 normalMatrix;',
+ 'uniform vec3 cameraPosition;',
+ 'uniform bool isOrthographic;',
+ '#ifdef USE_INSTANCING',
+ ' attribute mat4 instanceMatrix;',
+ '#endif',
+ '#ifdef USE_INSTANCING_COLOR',
+ ' attribute vec3 instanceColor;',
+ '#endif',
+
+ '#ifdef USE_INSTANCING_MORPH',
+ ' uniform sampler2D morphTexture;',
+ '#endif',
+
+ 'attribute vec3 position;',
+ 'attribute vec3 normal;',
+ 'attribute vec2 uv;',
+
+ '#ifdef USE_UV1',
+ ' attribute vec2 uv1;',
+ '#endif',
+ '#ifdef USE_UV2',
+ ' attribute vec2 uv2;',
+ '#endif',
+ '#ifdef USE_UV3',
+ ' attribute vec2 uv3;',
+ '#endif',
+
+ '#ifdef USE_TANGENT',
+ ' attribute vec4 tangent;',
+ '#endif',
+ '#if defined( USE_COLOR_ALPHA )',
+ ' attribute vec4 color;',
+ '#elif defined( USE_COLOR )',
+ ' attribute vec3 color;',
+ '#endif',
+ '#if ( defined( USE_MORPHTARGETS ) && ! defined( MORPHTARGETS_TEXTURE ) )',
+ ' attribute vec3 morphTarget0;',
+ ' attribute vec3 morphTarget1;',
+ ' attribute vec3 morphTarget2;',
+ ' attribute vec3 morphTarget3;',
+ ' #ifdef USE_MORPHNORMALS',
+ ' attribute vec3 morphNormal0;',
+ ' attribute vec3 morphNormal1;',
+ ' attribute vec3 morphNormal2;',
+ ' attribute vec3 morphNormal3;',
+ ' #else',
+ ' attribute vec3 morphTarget4;',
+ ' attribute vec3 morphTarget5;',
+ ' attribute vec3 morphTarget6;',
+ ' attribute vec3 morphTarget7;',
+ ' #endif',
+ '#endif',
+ '#ifdef USE_SKINNING',
+ ' attribute vec4 skinIndex;',
+ ' attribute vec4 skinWeight;',
+ '#endif',
+ '\n'
+ ].where((s) => filterEmptyLine(s)).join('\n');
+
+ prefixFragment = [
+ generatePrecision(parameters),
+
+ '#define SHADER_TYPE ${parameters.shaderType}',
+ '#define SHADER_NAME ${parameters.shaderName}',
+
+ customDefines,
+
+ parameters.useFog && parameters.fog ? '#define USE_FOG' : '',
+ parameters.useFog && parameters.fogExp2 ? '#define FOG_EXP2' : '',
+
+ parameters.alphaToCoverage ? '#define ALPHA_TO_COVERAGE' : '',
+ parameters.map ? '#define USE_MAP' : '',
+ parameters.matcap ? '#define USE_MATCAP' : '',
+ parameters.envMap ? '#define USE_ENVMAP' : '',
+ parameters.envMap ? '#define $envMapTypeDefine': '',
+ parameters.envMap ? '#define $envMapModeDefine': '',
+ parameters.envMap ? '#define $envMapBlendingDefine': '',
+ envMapCubeUVSize != null? '#define CUBEUV_TEXEL_WIDTH ${envMapCubeUVSize['texelWidth']}': '',
+ envMapCubeUVSize != null? '#define CUBEUV_TEXEL_HEIGHT ${envMapCubeUVSize['texelHeight']}' : '',
+ envMapCubeUVSize != null? '#define CUBEUV_MAX_MIP ${envMapCubeUVSize['maxMip']}.0' : '',
+
+ parameters.lightMap ? '#define USE_LIGHTMAP' : '',
+ parameters.aoMap ? '#define USE_AOMAP' : '',
+ parameters.bumpMap ? '#define USE_BUMPMAP' : '',
+ parameters.normalMap ? '#define USE_NORMALMAP' : '',
+ parameters.normalMapObjectSpace ? '#define USE_NORMALMAP_OBJECTSPACE' : '',
+ parameters.normalMapTangentSpace ? '#define USE_NORMALMAP_TANGENTSPACE' : '',
+ parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '',
+
+ parameters.anisotropy ? '#define USE_ANISOTROPY' : '',
+ parameters.anisotropyMap ? '#define USE_ANISOTROPYMAP' : '',
+
+ parameters.clearcoat ? '#define USE_CLEARCOAT' : '',
+ parameters.clearcoatMap ? '#define USE_CLEARCOATMAP' : '',
+ parameters.clearcoatRoughnessMap ? '#define USE_CLEARCOAT_ROUGHNESSMAP' : '',
+ parameters.clearcoatNormalMap ? '#define USE_CLEARCOAT_NORMALMAP' : '',
+
+ parameters.dispersion ? '#define USE_DISPERSION' : '',
+
+ parameters.iridescence ? '#define USE_IRIDESCENCE' : '',
+ parameters.iridescenceMap ? '#define USE_IRIDESCENCEMAP' : '',
+ parameters.iridescenceThicknessMap ? '#define USE_IRIDESCENCE_THICKNESSMAP' : '',
+
+ parameters.specularMap ? '#define USE_SPECULARMAP' : '',
+ parameters.specularColorMap ? '#define USE_SPECULAR_COLORMAP' : '',
+ parameters.specularIntensityMap ? '#define USE_SPECULAR_INTENSITYMAP' : '',
+
+ parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '',
+ parameters.metalnessMap ? '#define USE_METALNESSMAP' : '',
+
+ parameters.alphaMap ? '#define USE_ALPHAMAP' : '',
+ parameters.alphaTest ? '#define USE_ALPHATEST' : '',
+ parameters.alphaHash ? '#define USE_ALPHAHASH' : '',
+
+ parameters.sheen ? '#define USE_SHEEN' : '',
+ parameters.sheenColorMap ? '#define USE_SHEEN_COLORMAP' : '',
+ parameters.sheenRoughnessMap ? '#define USE_SHEEN_ROUGHNESSMAP' : '',
+
+ parameters.transmission ? '#define USE_TRANSMISSION' : '',
+ parameters.transmissionMap ? '#define USE_TRANSMISSIONMAP' : '',
+ parameters.thicknessMap ? '#define USE_THICKNESSMAP' : '',
+
+ parameters.vertexTangents && !parameters.flatShading? '#define USE_TANGENT' : '',
+ parameters.vertexColors || parameters.instancingColor ? '#define USE_COLOR' : '',
+ parameters.vertexAlphas ? '#define USE_COLOR_ALPHA' : '',
+ parameters.vertexUv1s ? '#define USE_UV1' : '',
+ parameters.vertexUv2s ? '#define USE_UV2' : '',
+ parameters.vertexUv3s ? '#define USE_UV3' : '',
+
+ parameters.pointsUvs ? '#define USE_POINTS_UV' : '',
+
+ parameters.gradientMap ? '#define USE_GRADIENTMAP' : '',
+
+ parameters.flatShading ? '#define FLAT_SHADED' : '',
+
+ parameters.doubleSided ? '#define DOUBLE_SIDED' : '',
+ parameters.flipSided ? '#define FLIP_SIDED' : '',
+
+ parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '',
+ parameters.shadowMapEnabled ? '#define $shadowMapTypeDefine': '',
+
+ parameters.premultipliedAlpha ? '#define PREMULTIPLIED_ALPHA' : '',
+
+ parameters.numLightProbes > 0 ? '#define USE_LIGHT_PROBES' : '',
+
+ parameters.useLegacyLights ? '#define LEGACY_LIGHTS' : '',
+
+ parameters.decodeVideoTexture ? '#define DECODE_VIDEO_TEXTURE' : '',
+
+ parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '',
+
+ 'uniform mat4 viewMatrix;',
+ 'uniform vec3 cameraPosition;',
+ 'uniform bool isOrthographic;',
+
+ ( parameters.toneMapping != NoToneMapping ) ? '#define TONE_MAPPING' : '',
+ ( parameters.toneMapping != NoToneMapping ) ? shaderChunk[ 'tonemapping_pars_fragment' ] : '', // this code is required here because it is used by the toneMapping() function defined below
+ ( parameters.toneMapping != NoToneMapping ) ? getToneMappingFunction( 'toneMapping', parameters.toneMapping ) : '',
+
+ parameters.dithering ? '#define DITHERING' : '',
+ parameters.opaque ? '#define OPAQUE' : '',
+
+ shaderChunk[ 'colorspace_pars_fragment' ], // this code is required here because it is used by the various encoding/decoding function defined below
+ getTexelEncodingFunction( 'linearToOutputTexel', parameters.outputColorSpace ),
+
+ parameters.useDepthPacking ? '#define DEPTH_PACKING ${parameters.depthPacking}': '',
+
+ '\n'
+ ].where((s) => filterEmptyLine(s)).join('\n');
+ }
+
+ vertexShader = resolveIncludes(vertexShader);
+ vertexShader = replaceLightNums(vertexShader, parameters);
+ vertexShader = replaceClippingPlaneNums(vertexShader, parameters);
+
+ fragmentShader = resolveIncludes(fragmentShader);
+ fragmentShader = replaceLightNums(fragmentShader, parameters);
+ fragmentShader = replaceClippingPlaneNums(fragmentShader, parameters);
+
+ vertexShader = unrollLoops(vertexShader);
+ fragmentShader = unrollLoops(fragmentShader);
+
+ if (!parameters.isRawShaderMaterial) {
+ // GLSL 3.0 conversion for built-in materials and ShaderMaterial
+ versionString = "#version 300 es\n";
+
+ prefixVertex = '${[
+ customVertexExtensions,
+ '#define attribute in',
+ '#define varying out',
+ '#define texture2D texture'
+ ].join('\n')}\n$prefixVertex';
+
+ prefixFragment = '${[
+ '#define varying in',
+ (parameters.glslVersion == GLSL3) ? '' : 'layout(location = 0) out highp vec4 pc_fragColor;',
+ (parameters.glslVersion == GLSL3) ? '' : '#define gl_FragColor pc_fragColor',
+ '#define gl_FragDepthEXT gl_FragDepth',
+ '#define texture2D texture',
+ '#define textureCube texture',
+ '#define texture2DProj textureProj',
+ '#define texture2DLodEXT textureLod',
+ '#define texture2DProjLodEXT textureProjLod',
+ '#define textureCubeLodEXT textureLod',
+ '#define texture2DGradEXT textureGrad',
+ '#define texture2DProjGradEXT textureProjGrad',
+ '#define textureCubeGradEXT textureGrad'
+ ].join('\n')}\n$prefixFragment';
+ }
+
+ String vertexGlsl = versionString + prefixVertex + vertexShader;
+ String fragmentGlsl = versionString + prefixFragment + fragmentShader;
+
+ final glVertexShader = AngleShader(gl, WebGL.VERTEX_SHADER, vertexGlsl);
+ final glFragmentShader = AngleShader(gl, WebGL.FRAGMENT_SHADER, fragmentGlsl);
+
+ gl.attachShader(program!, glVertexShader.shader);
+ gl.attachShader(program!, glFragmentShader.shader);
+
+ // Force a particular attribute to index 0.
+
+ if (parameters.index0AttributeName != null) {
+ gl.bindAttribLocation(program!, 0, parameters.index0AttributeName ?? '');
+ } else if (parameters.morphTargets == true) {
+ // programs with morphTargets displace position out of attribute 0
+ gl.bindAttribLocation(program!, 0, 'position');
+ }
+
+ gl.linkProgram(program!);
+ onFirstUse = (AngleProgram self ) {
+ // check for link errors
+ if (renderer.debug["checkShaderErrors"]) {
+ final programLog = gl.getProgramInfoLog(program!)?.trim();
+ final vertexLog = gl.getShaderInfoLog(glVertexShader.shader)?.trim();
+ final fragmentLog = gl.getShaderInfoLog(glFragmentShader.shader)?.trim();
+
+ bool runnable = true;
+ bool haveDiagnostics = true;
+
+ if (gl.getProgramParameter(program!, WebGL.LINK_STATUS).id == 0) {
+ runnable = false;
+
+ final vertexErrors = getShaderErrors(gl, glVertexShader, 'vertex');
+ final fragmentErrors = getShaderErrors(gl, glFragmentShader, 'fragment');
+
+ console.error('WebGLProgram: shader error: ${gl.getError()} gl.VALIDATE_STATUS ${gl.getProgramParameter(program!, WebGL.VALIDATE_STATUS)} gl.getProgramInfoLog $programLog $vertexErrors $fragmentErrors ');
+ } else if (programLog != '' && programLog != null) {
+ console.error('WebGLProgram: gl.getProgramInfoLog() programLog: $programLog vertexLog: $vertexLog fragmentLog: $fragmentLog ');
+ } else if (vertexLog == '' || fragmentLog == '') {
+ haveDiagnostics = false;
+ }
+
+ if (haveDiagnostics) {
+ self.diagnostics = {
+ "runnable": runnable,
+ "programLog": programLog,
+ "vertexShader": {"log": vertexLog, "prefix": prefixVertex},
+ "fragmentShader": {"log": fragmentLog, "prefix": prefixFragment}
+ };
+ }
+ }
+
+ gl.deleteShader(glVertexShader.shader);
+ gl.deleteShader(glFragmentShader.shader);
+
+ cachedUniforms = AngleUniforms( gl, this );
+ cachedAttributes = fetchAttributeLocations( gl, program! );
+ };
+ }
+ // set up caching for attribute locations
+
+ AngleUniforms getUniforms() {
+ if ( cachedUniforms == null ) {
+ onFirstUse.call(this);
+ }
+ return cachedUniforms!;
+ }
+
+ Map getAttributes() {
+ if ( cachedUniforms == null ) {
+ onFirstUse.call(this);
+ }
+
+ return cachedAttributes!;
+ }
+
+ bool get programReady => ( parameters.rendererExtensionParallelShaderCompile == false );
+ set programReady(value){
+ return value;
+ }
+ bool isReady() {
+ if ( programReady == false ) {
+ programReady = gl.getProgramParameter( program!, COMPLETION_STATUS_KHR ).id;
+ }
+ return programReady;
+ }
+
+ // free resource
+ void destroy() {
+ bindingStates.releaseStatesOfProgram(this);
+ gl.deleteProgram(program!);
+ program = null;
+ }
+}
diff --git a/packages/three_js_angle_renderer/lib/angle/angle_program_extra.dart b/packages/three_js_angle_renderer/lib/angle/angle_program_extra.dart
new file mode 100755
index 00000000..9f9502d3
--- /dev/null
+++ b/packages/three_js_angle_renderer/lib/angle/angle_program_extra.dart
@@ -0,0 +1,424 @@
+part of three_webgl;
+
+class AttributeLocations{
+ AttributeLocations({
+ this.location,
+ this.locationSize = 0,
+ this.type = 0
+ });
+
+ final dynamic location;
+ final int type;//: info.type,
+ //"location": gl.getAttribLocation(program, name),
+ final int locationSize;//: locationSize
+}
+
+mixin AngleProgramExtra {
+ String handleSource(String? string, int errorLine) {
+ final lines = string?.split('\n') ?? [];
+ final lines2 = [];
+
+ int from = math.max(errorLine - 6, 0);
+ int to = math.min(errorLine + 6, lines.length);
+
+ for (int i = 0; i < lines.length; i++) {
+ lines[i] = "${(i + 1)}: ${lines[i]}";
+ }
+
+ for (int i = from; i < to; i++) {
+ lines2.add("${(i + 1)}: ${lines[i]}");
+ }
+
+ return lines2.join('\n');
+ }
+
+ List getEncodingComponents(String colorSpace) {
+ final workingPrimaries = ColorManagement.getPrimaries(ColorManagement.workingColorSpace);
+ final encodingPrimaries = ColorManagement.getPrimaries(ColorSpace.fromString(colorSpace));
+
+ String gamutMapping = '';
+
+ if ( workingPrimaries == encodingPrimaries ) {
+ gamutMapping = '';
+ } else if ( workingPrimaries == P3Primaries && encodingPrimaries == Rec709Primaries ) {
+ gamutMapping = 'LinearDisplayP3ToLinearSRGB';
+ } else if ( workingPrimaries == Rec709Primaries && encodingPrimaries == P3Primaries ) {
+ gamutMapping = 'LinearSRGBToLinearDisplayP3';
+ }
+
+ switch ( colorSpace ) {
+ case LinearSRGBColorSpace:
+ case LinearDisplayP3ColorSpace:
+ return [ gamutMapping, 'LinearTransferOETF' ];
+ case SRGBColorSpace:
+ case DisplayP3ColorSpace:
+ return [ gamutMapping, 'sRGBTransferOETF' ];
+ default:
+ console.warning( 'THREE.WebGLProgram: Unsupported color space: $colorSpace');
+ return [ gamutMapping, 'LinearTransferOETF' ];
+ }
+ }
+
+ String getShaderErrors(RenderingContext gl, AngleShader shader, type) {
+ final status = gl.getShaderParameter(shader.shader, WebGL.COMPILE_STATUS);
+ final errors = (gl.getShaderInfoLog(shader.shader)??'').trim();
+
+ if (status && errors == '') return '';
+
+ final regExp = RegExp(r"ERROR: 0:(\d+)");
+ final match = regExp.firstMatch(errors);
+
+ int errorLine = int.parse(match!.group(1)!);
+
+ // --enable-privileged-webgl-extension
+ // console.log( '**' + type + '**', gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( shader ) );
+
+ final source = gl.getShaderSource(shader.shader);
+
+ return 'three.WebGLShader: gl.getShaderInfoLog() $type\n$errors\n${handleSource(source, errorLine)}';
+ }
+
+ String getTexelEncodingFunction(String functionName, String encoding) {
+ final components = getEncodingComponents(encoding);
+ return 'vec4 $functionName( vec4 value ) { return ${components[ 0 ]}( ${components[ 1 ]}( value ) ); }';
+ }
+
+ String getToneMappingFunction(functionName, toneMapping) {
+ String toneMappingName;
+
+ switch (toneMapping) {
+ case LinearToneMapping:
+ toneMappingName = 'Linear';
+ break;
+
+ case ReinhardToneMapping:
+ toneMappingName = 'Reinhard';
+ break;
+
+ case CineonToneMapping:
+ toneMappingName = 'OptimizedCineon';
+ break;
+
+ case ACESFilmicToneMapping:
+ toneMappingName = 'ACESFilmic';
+ break;
+
+ case AgXToneMapping:
+ toneMappingName = 'AgX';
+ break;
+
+ case NeutralToneMapping:
+ toneMappingName = 'Neutral';
+ break;
+
+ case CustomToneMapping:
+ toneMappingName = 'Custom';
+ break;
+
+ default:
+ console.error('three.WebGLProgram: Unsupported toneMapping: $toneMapping');
+ toneMappingName = 'Linear';
+ }
+
+ return 'vec3 $functionName( vec3 color ) { return ${toneMappingName}ToneMapping( color ); }';
+ }
+
+ String generateVertexExtensions(Parameters parameters) {
+ final chunks = [
+ parameters.extensionClipCullDistance ? '#extension GL_ANGLE_clip_cull_distance : require' : '',
+ parameters.extensionMultiDraw ? '#extension GL_ANGLE_multi_draw : require' : '',
+ ];
+
+ return chunks.where((s) => filterEmptyLine(s)).join('\n');
+ }
+
+ String generateDefines(defines) {
+ final chunks = [];
+
+ if (defines != null) {
+ for (final name in defines.keys) {
+ final value = defines[name];
+
+ if (value == false) continue;
+
+ // print("WebGLProgramExtra generateDefines name: ${name} value: ${value} ");
+ chunks.add('#define $name $value');
+ }
+ }
+
+ return chunks.join('\n');
+ }
+
+ Map fetchAttributeLocations(RenderingContext gl, Program program) {
+ Map attributes = {};
+
+ final n = gl.getProgramParameter(program, WebGL.ACTIVE_ATTRIBUTES).id;
+
+ for (int i = 0; i < n; i++) {
+ final info = gl.getActiveAttrib(program, i);
+ final name = info.name;
+
+ // print( "three.WebGLProgram: ACTIVE VERTEX ATTRIBUTE: name: ${name} i: ${i}");
+
+ // attributes[name] = gl.getAttribLocation(program, name);
+
+ int locationSize = 1;
+ if (info.type == WebGL.FLOAT_MAT2) locationSize = 2;
+ if (info.type == WebGL.FLOAT_MAT3) locationSize = 3;
+ if (info.type == WebGL.FLOAT_MAT4) locationSize = 4;
+
+ // console.log( 'three.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:', name, i );
+
+ attributes[name] = AttributeLocations(
+ type: info.type,
+ location: gl.getAttribLocation(program, name),
+ locationSize: locationSize
+ );
+ }
+
+ return attributes;
+ }
+
+ bool filterEmptyLine(string) {
+ return string != '';
+ }
+
+ String replaceLightNums(String string, Parameters parameters) {
+ final numSpotLightCoords = parameters.numSpotLightShadows + parameters.numSpotLightMaps - parameters.numSpotLightShadowsWithMaps;
+
+ string = string.replaceAll("NUM_DIR_LIGHTS", parameters.numDirLights.toString() );
+ string = string.replaceAll("NUM_SPOT_LIGHTS", parameters.numSpotLights.toString() );
+ string = string.replaceAll("NUM_SPOT_LIGHT_MAPS", parameters.numSpotLightMaps.toString() );
+ string = string.replaceAll("NUM_SPOT_LIGHT_COORDS", numSpotLightCoords.toString() );
+ string = string.replaceAll("NUM_RECT_AREA_LIGHTS", parameters.numRectAreaLights.toString() );
+ string = string.replaceAll("NUM_POINT_LIGHTS", parameters.numPointLights.toString() );
+ string = string.replaceAll("NUM_HEMI_LIGHTS", parameters.numHemiLights.toString() );
+ string = string.replaceAll("NUM_DIR_LIGHT_SHADOWS", parameters.numDirLightShadows.toString() );
+ string = string.replaceAll("NUM_SPOT_LIGHT_SHADOWS_WITH_MAPS", parameters.numSpotLightShadowsWithMaps.toString() );
+ string = string.replaceAll("NUM_SPOT_LIGHT_SHADOWS", parameters.numSpotLightShadows.toString() );
+ string = string.replaceAll("NUM_POINT_LIGHT_SHADOWS", parameters.numPointLightShadows.toString() );
+
+ return string;
+ }
+
+ String replaceClippingPlaneNums(String string, Parameters parameters) {
+ string = string.replaceAll("NUM_CLIPPING_PLANES", parameters.numClippingPlanes.toString());
+ string = string.replaceAll("UNION_CLIPPING_PLANES", (parameters.numClippingPlanes - parameters.numClipIntersection).toString());
+
+ return string;
+ }
+
+ // Resolve Includes
+
+ final includePattern = RegExp(r"[ \t]*#include +<([\w\d./]+)>"); //gm;
+
+ String resolveIncludes(String string) {
+ // return string.replaceAll(includePattern, includeReplacer);
+
+ // Loop through all matches.
+ for (final match in includePattern.allMatches(string)) {
+ /// Returns the string matched by the given [group].
+ ///
+ /// If [group] is 0, returns the match of the pattern.
+ ///
+ /// The result may be `null` if the pattern didn't assign a value to it
+ /// as part of this match.
+ // print(" resolveIncludes ");
+ // print(match.group(0)); // 15, then 20
+
+ String includeString = match.group(1)!;
+
+ // print(" includeString: ${includeString} ");
+
+ String targetString = shaderChunk[includeString]!;
+
+ String targetString2 = resolveIncludes(targetString);
+
+ String fromString = match.group(0)!;
+
+ string = string.replaceFirst(fromString, targetString2);
+ }
+
+ return string;
+ }
+
+ final shaderChunkMap = {};
+
+ String includeReplacer(match, include) {
+ String? string = shaderChunk[ include ];
+
+ if (string == null) {
+ final newInclude = shaderChunkMap[include];
+ if ( newInclude != null ) {
+ string = shaderChunk[ newInclude ];
+ console.warning( 'THREE.WebGLRenderer: Shader chunk "$include" has been deprecated. Use "$newInclude" instead.');
+ } else {
+ throw( 'Can not resolve #include <$include>' );
+ }
+ }
+
+ return resolveIncludes( string! );
+ }
+
+// Unroll Loops
+
+ final unrollLoopPattern = RegExp(r"#pragma unroll_loop_start\s+for\s*\(\s*int\s+i\s*=\s*(\d+)\s*;\s*i\s*<\s*(\d+)\s*;\s*i\s*\+\+\s*\)\s*{([\s\S]+?)}\s+#pragma unroll_loop_end");
+
+ String unrollLoops(String string) {
+ string = unrollLoopPatternReplace(string);
+ return string;
+ }
+
+ String unrollLoopPatternReplace(String string) {
+ final matches = unrollLoopPattern.allMatches(string);
+
+ for (final match in matches) {
+ String stringResult = '';
+
+ int start = int.parse(match.group(1)!);
+ int end = int.parse(match.group(2)!);
+ final snippet = match.group(3)!;
+
+ for (int i = start; i < end; i++) {
+ String snippet2 = snippet.replaceAll(RegExp(r"\[\s*i\s*\]"), "[$i]");
+ snippet2 = snippet2.replaceAll(RegExp(r"UNROLLED_LOOP_INDEX"), i.toString());
+ stringResult = stringResult + snippet2;
+ }
+
+ string = string.replaceFirst(match.group(0)!, stringResult);
+ }
+ return string;
+ }
+
+ String loopReplacer(match, s, e, snippet) {
+ String string = '';
+
+ int start = int.parse(s);
+ int end = int.parse(e);
+
+ for (int i = start; i < end; i++) {
+ snippet = snippet
+ ..replaceAll(RegExp(r"\[\s*i\s*\]"), '[ $i ]')
+ ..replaceAll(RegExp(r"UNROLLED_LOOP_INDEX"), i);
+
+ string += snippet;
+ }
+
+ return string;
+ }
+
+//
+
+ String generatePrecision(Parameters parameters) {
+ String precisionstring = '''precision ${parameters.precision} float;
+ precision ${parameters.precision} int;
+ precision ${parameters.precision} sampler2D;
+ precision ${parameters.precision} samplerCube;
+ precision ${parameters.precision} sampler3D;
+ precision ${parameters.precision} sampler2DArray;
+ precision ${parameters.precision} sampler2DShadow;
+ precision ${parameters.precision} samplerCubeShadow;
+ precision ${parameters.precision} sampler2DArrayShadow;
+ precision ${parameters.precision} isampler2D;
+ precision ${parameters.precision} isampler3D;
+ precision ${parameters.precision} isamplerCube;
+ precision ${parameters.precision} isampler2DArray;
+ precision ${parameters.precision} usampler2D;
+ precision ${parameters.precision} usampler3D;
+ precision ${parameters.precision} usamplerCube;
+ precision ${parameters.precision} usampler2DArray;
+ ''';
+
+ if ( parameters.precision == 'highp' ) {
+ precisionstring += '\n#define HIGH_PRECISION';
+ } else if ( parameters.precision == 'mediump' ) {
+ precisionstring += '\n#define MEDIUM_PRECISION';
+ } else if ( parameters.precision == 'lowp' ) {
+ precisionstring += '\n#define LOW_PRECISION';
+ }
+
+ return precisionstring;
+ }
+
+ String generateShadowMapTypeDefine(Parameters parameters) {
+ String shadowMapTypeDefine = 'SHADOWMAP_TYPE_BASIC';
+
+ if (parameters.shadowMapType == PCFShadowMap) {
+ shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF';
+ } else if (parameters.shadowMapType == PCFSoftShadowMap) {
+ shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF_SOFT';
+ } else if (parameters.shadowMapType == VSMShadowMap) {
+ shadowMapTypeDefine = 'SHADOWMAP_TYPE_VSM';
+ }
+
+ return shadowMapTypeDefine;
+ }
+
+ String generateEnvMapTypeDefine(Parameters parameters) {
+ String envMapTypeDefine = 'ENVMAP_TYPE_CUBE';
+
+ if (parameters.envMap) {
+ switch (parameters.envMapMode) {
+ case CubeReflectionMapping:
+ case CubeRefractionMapping:
+ envMapTypeDefine = 'ENVMAP_TYPE_CUBE';
+ break;
+
+ case CubeUVReflectionMapping:
+ envMapTypeDefine = 'ENVMAP_TYPE_CUBE_UV';
+ break;
+ }
+ }
+
+ return envMapTypeDefine;
+ }
+
+ String generateEnvMapModeDefine (Parameters parameters) {
+ String envMapModeDefine = 'ENVMAP_MODE_REFLECTION';
+
+ if (parameters.envMap) {
+ switch (parameters.envMapMode) {
+ case CubeRefractionMapping:
+ envMapModeDefine = 'ENVMAP_MODE_REFRACTION';
+ break;
+ }
+ }
+
+ return envMapModeDefine;
+ }
+
+ String generateEnvMapBlendingDefine(Parameters parameters) {
+ String envMapBlendingDefine = 'ENVMAP_BLENDING_NONE';
+
+ if (parameters.envMap) {
+ switch (parameters.combine) {
+ case MultiplyOperation:
+ envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY';
+ break;
+
+ case MixOperation:
+ envMapBlendingDefine = 'ENVMAP_BLENDING_MIX';
+ break;
+
+ case AddOperation:
+ envMapBlendingDefine = 'ENVMAP_BLENDING_ADD';
+ break;
+ }
+ }
+
+ return envMapBlendingDefine;
+ }
+
+ Map? generateCubeUVSize(Parameters parameters) {
+ final imageHeight = parameters.envMapCubeUVHeight;
+
+ if (imageHeight == null) return null;
+
+ int maxMip = MathUtils.log2(imageHeight).toInt() - 2;
+
+ final texelHeight = 1.0 / imageHeight;
+ final texelWidth = 1.0 / (3 * math.max(math.pow(2, maxMip), 7 * 16));
+
+ return {"texelWidth": texelWidth, "texelHeight": texelHeight, "maxMip": maxMip};
+ }
+}
diff --git a/packages/three_js_angle_renderer/lib/angle/angle_programs.dart b/packages/three_js_angle_renderer/lib/angle/angle_programs.dart
new file mode 100755
index 00000000..a2f48d9a
--- /dev/null
+++ b/packages/three_js_angle_renderer/lib/angle/angle_programs.dart
@@ -0,0 +1,509 @@
+part of three_webgl;
+
+class AnglePrograms {
+ final shaderIDs = {
+ "MeshDepthMaterial": 'depth',
+ "MeshDistanceMaterial": 'distanceRGBA',
+ "MeshNormalMaterial": 'normal',
+ "MeshBasicMaterial": 'basic',
+ "MeshLambertMaterial": 'lambert',
+ "MeshPhongMaterial": 'phong',
+ "MeshToonMaterial": 'toon',
+ "MeshStandardMaterial": 'physical',
+ "MeshPhysicalMaterial": 'physical',
+ "MeshMatcapMaterial": 'matcap',
+ "LineBasicMaterial": 'basic',
+ "LineDashedMaterial": 'dashed',
+ "PointsMaterial": 'points',
+ "ShadowMaterial": 'shadow',
+ "SpriteMaterial": 'sprite'
+ };
+
+ AngleRenderer renderer;
+ AngleCubeMaps cubemaps;
+ AngleCubeUVMaps cubeuvmaps;
+ AngleExtensions extensions;
+ AngleCapabilities capabilities;
+ AngleBindingStates bindingStates;
+ AngleClipping clipping;
+
+ final _programLayers = Layers();
+ final _customShaders = AngleShaderCache();
+ List programs = [];
+ final List _activeChannels = [];
+
+ late bool logarithmicDepthBuffer;
+ late bool vertexTextures;
+ late String precision;
+ bool _didDispose = false;
+
+ AnglePrograms(this.renderer, this.cubemaps, this.cubeuvmaps, this.extensions, this.capabilities, this.bindingStates, this.clipping) {
+ logarithmicDepthBuffer = capabilities.logarithmicDepthBuffer;
+ vertexTextures = capabilities.vertexTextures;
+
+ precision = capabilities.precision;
+ }
+
+ void dispose(){
+ if(_didDispose) return;
+ _didDispose = true;
+ renderer.dispose();
+ cubemaps.dispose();
+ extensions.dispose();
+ capabilities.dispose();
+ bindingStates.dispose();
+ clipping.dispose();
+ _customShaders.dispose();
+ programs.clear();
+ }
+
+ String getChannel(int value ) {
+ _activeChannels.add( value );
+ if ( value == 0 ) return 'uv';
+ return 'uv$value';
+ }
+
+ Parameters getParameters(Material material, LightState lights, List shadows, Scene scene, Object3D object) {
+ final fog = scene.fog;
+ final geometry = object.geometry;
+ final environment = material is MeshStandardMaterial ? scene.environment : null;
+
+ Texture? envMap = material is MeshStandardMaterial?cubeuvmaps.get(material.envMap ?? environment):cubemaps.get(material.envMap ?? environment);
+
+ final envMapCubeUVHeight = (envMap != null) && (envMap.mapping == CubeUVReflectionMapping) ? envMap.image?.height : null;
+
+ final shaderID = shaderIDs[material.shaderID];
+
+ // heuristics to create shader parameters according to lights in the scene
+ // (not to blow over maxLights budget)
+
+ if (material.precision != null) {
+ precision = capabilities.getMaxPrecision(material.precision);
+
+ if (precision != material.precision) {
+ console.warning('WebGLProgram.getParameters: ${material.precision} not supported, using $precision instead.');
+ }
+ }
+
+ final morphAttribute = geometry?.morphAttributes["position"] ?? geometry?.morphAttributes["normal"] ?? geometry?.morphAttributes["color"];
+ final morphTargetsCount = (morphAttribute != null) ? morphAttribute.length : 0;
+
+ int morphTextureStride = 0;
+
+ if (geometry?.morphAttributes["position"] != null) morphTextureStride = 1;
+ if (geometry?.morphAttributes["normal"] != null) morphTextureStride = 2;
+ if (geometry?.morphAttributes["color"] != null) morphTextureStride = 3;
+
+ //
+
+ String? vertexShader, fragmentShader;
+ dynamic customVertexShaderID;
+ dynamic customFragmentShaderID;
+
+ if (shaderID != null) {
+ final shader = shaderLib[shaderID];
+ vertexShader = shader["vertexShader"];
+ fragmentShader = shader["fragmentShader"];
+ } else {
+ vertexShader = material.vertexShader;
+ fragmentShader = material.fragmentShader;
+
+ _customShaders.update(material);
+
+ customVertexShaderID = _customShaders.getVertexShaderID(material);
+ customFragmentShaderID = _customShaders.getFragmentShaderID(material);
+ }
+
+ // print(" WebGLPrograms material : ${material.type} ${material.shaderID} ${material.id} object: ${object.type} ${object.id} shaderID: ${shaderID} vertexColors: ${material.vertexColors} ");
+
+ final currentRenderTarget = renderer.getRenderTarget();
+
+ final useAlphaTest = material.alphaTest > 0;
+ final useClearcoat = material.clearcoat > 0;
+
+ final parameters = Parameters(
+ shaderID: shaderID,
+ shaderType: material.type,
+ shaderName: "${material.type} - ${material.name}",
+
+ vertexShader: vertexShader ?? '',
+ fragmentShader: fragmentShader ?? '',
+ defines: material.defines,
+
+ customVertexShaderID: customVertexShaderID,
+ customFragmentShaderID: customFragmentShaderID,
+
+ isRawShaderMaterial: material is RawShaderMaterial,
+ glslVersion: material.glslVersion,
+
+ precision: precision,
+
+ batching: object is BatchedMesh,
+ instancing: object is InstancedMesh,
+ instancingColor: object is InstancedMesh && object.instanceColor != null,
+ instancingMorph: object is InstancedMesh && object.morphTexture != null,
+
+ supportsVertexTextures: vertexTextures,
+ outputColorSpace: ( currentRenderTarget == null ) ? renderer.outputColorSpace : ( currentRenderTarget.isXRRenderTarget? currentRenderTarget.texture.colorSpace : LinearSRGBColorSpace ),
+ alphaToCoverage: !! material.alphaToCoverage,
+
+ map: material.map != null,
+ matcap: material.matcap != null,
+ envMap: envMap != null,
+ envMapMode: envMap?.mapping,
+ envMapCubeUVHeight: envMapCubeUVHeight,
+ aoMap: material.aoMap != null,
+ lightMap: material.lightMap != null,
+ bumpMap: material.bumpMap != null,
+ normalMap: material.normalMap != null,
+ displacementMap: capabilities.vertexTextures && material.displacementMap != null,
+ emissiveMap: material.emissiveMap != null,
+
+ normalMapObjectSpace: material.normalMap != null && material.normalMapType == ObjectSpaceNormalMap,
+ normalMapTangentSpace: material.normalMap != null && material.normalMapType == TangentSpaceNormalMap,
+
+ metalnessMap: material.metalnessMap != null,
+ roughnessMap: material.roughnessMap != null,
+
+ anisotropy: material is MeshPhysicalMaterial && material.anisotropy > 0,
+ anisotropyMap: material is MeshPhysicalMaterial && material.anisotropy > 0 && material.anisotropyMap != null,
+
+ clearcoat: useClearcoat,
+ clearcoatMap: useClearcoat && material.clearcoatMap != null,
+ clearcoatNormalMap: useClearcoat && material.clearcoatRoughnessMap != null,
+ clearcoatRoughnessMap: useClearcoat && material.clearcoatNormalMap != null,
+
+ dispersion: material is MeshPhysicalMaterial && material.dispersion > 0,
+
+ iridescence: material is MeshPhysicalMaterial && material.iridescence > 0,
+ iridescenceMap: material is MeshPhysicalMaterial && material.iridescence > 0 && material.iridescenceMap != null,
+ iridescenceThicknessMap: material is MeshPhysicalMaterial && material.iridescence > 0 && material.iridescenceThicknessMap != null,
+
+ sheen: material.sheen > 0,
+ sheenColorMap: material.sheenColorMap != null,
+ sheenRoughnessMap: material.sheenRoughnessMap != null,
+
+ specularMap: material.specularMap != null,
+ specularColorMap: material.specularColorMap != null,
+ specularIntensityMap: material.specularIntensityMap != null,
+
+ transmission: material.transmission > 0,
+ transmissionMap: material.transmissionMap != null,
+ thicknessMap: material.thicknessMap != null,
+
+ gradientMap: material.gradientMap != null,
+
+ opaque: !material.transparent && material.blending == NormalBlending && !material.alphaToCoverage,
+
+ alphaMap: material.alphaMap != null,
+ alphaTest: useAlphaTest,
+ alphaHash: material.alphaHash,
+
+ combine: material.combine,
+
+ //
+
+ mapUv: material.map ==null?null: getChannel( material.map!.channel ),
+ aoMapUv: material.aoMap ==null?null:getChannel( material.aoMap!.channel ),
+ lightMapUv: material.lightMap ==null?null:getChannel( material.lightMap!.channel ),
+ bumpMapUv: material.bumpMap ==null?null:getChannel( material.bumpMap!.channel ),
+ normalMapUv:material.normalMap ==null?null:getChannel( material.normalMap!.channel ),
+ displacementMapUv: material.displacementMap ==null?null:getChannel( material.displacementMap!.channel ),
+ emissiveMapUv: material.emissiveMap ==null?null:getChannel( material.emissiveMap!.channel ),
+
+ metalnessMapUv: material.metalnessMap ==null?null:getChannel( material.metalnessMap!.channel ),
+ roughnessMapUv: material.roughnessMap ==null?null:getChannel( material.roughnessMap!.channel ),
+
+ anisotropyMapUv: material.anisotropyMap==null?null:getChannel( material.anisotropyMap!.channel ),
+
+ clearcoatMapUv: material.clearcoatMap ==null?null:getChannel( material.clearcoatMap!.channel ),
+ clearcoatNormalMapUv: material.clearcoatNormalMap ==null?null:getChannel( material.clearcoatNormalMap!.channel ),
+ clearcoatRoughnessMapUv: material.clearcoatRoughnessMap ==null?null:getChannel( material.clearcoatRoughnessMap!.channel ),
+
+ iridescenceMapUv: material.iridescenceMap ==null?null:getChannel( material.iridescenceMap!.channel ),
+ iridescenceThicknessMapUv: material.iridescenceThicknessMap ==null?null:getChannel( material.iridescenceThicknessMap!.channel ),
+
+ sheenColorMapUv: material.sheenColorMap ==null?null:getChannel( material.sheenColorMap!.channel ),
+ sheenRoughnessMapUv: material.sheenRoughnessMap ==null?null:getChannel( material.sheenRoughnessMap!.channel ),
+
+ specularMapUv: material.specularMap ==null?null:getChannel( material.specularMap!.channel ),
+ specularColorMapUv: material.specularColorMap ==null?null:getChannel( material.specularColorMap!.channel ),
+ specularIntensityMapUv: material.specularIntensityMap ==null?null:getChannel( material.specularIntensityMap!.channel ),
+
+ transmissionMapUv: material.transmissionMap == null?null:getChannel( material.transmissionMap!.channel ),
+ thicknessMapUv: material.thicknessMap ==null?null:getChannel( material.thicknessMap!.channel ),
+
+ alphaMapUv: material.alphaMap ==null?null:getChannel( material.alphaMap!.channel ),
+
+ //
+
+ vertexTangents: (material.normalMap != null && geometry != null && geometry.attributes["tangent"] != null),
+ vertexColors: material.vertexColors,
+ vertexAlphas: material.vertexColors == true &&
+ geometry != null &&
+ geometry.attributes["color"] != null &&
+ geometry.attributes["color"].itemSize == 4,
+
+ pointsUvs: object is Points && geometry?.attributes['uv'] != null && ( material.map != null || material.alphaMap != null ),
+
+ fog: fog != null,
+ useFog: material.fog,
+ fogExp2: (fog != null && fog.isFogExp2),
+
+ flatShading: material.flatShading,
+
+ sizeAttenuation: material.sizeAttenuation,
+ logarithmicDepthBuffer: logarithmicDepthBuffer,
+
+ skinning: object is SkinnedMesh,
+
+ morphTargets: geometry?.morphAttributes['position'] != null,
+ morphNormals: geometry?.morphAttributes['normal'] != null,
+ morphColors: geometry?.morphAttributes['color'] != null,
+ morphTargetsCount: morphTargetsCount,
+ morphTextureStride: morphTextureStride,
+
+ numDirLights: lights.directional.length,
+ numPointLights: lights.point.length,
+ numSpotLights: lights.spot.length,
+ numSpotLightMaps: lights.spotLightMap.length,
+ numRectAreaLights: lights.rectArea.length,
+ numHemiLights: lights.hemi.length,
+
+ numDirLightShadows: lights.directionalShadowMap.length,
+ numPointLightShadows: lights.pointShadowMap.length,
+ numSpotLightShadows: lights.spotShadowMap.length,
+ numSpotLightShadowsWithMaps: lights.numSpotLightShadowsWithMaps,
+
+ numLightProbes: lights.numLightProbes,
+
+ numClippingPlanes: clipping.numPlanes,
+ numClipIntersection: clipping.numIntersection,
+
+ dithering: material.dithering,
+
+ shadowMapEnabled: renderer.shadowMap.enabled && shadows.length > 0,
+ shadowMapType: renderer.shadowMap.type,
+
+ toneMapping: material.toneMapped ? renderer.toneMapping : NoToneMapping,
+ useLegacyLights: renderer.useLegacyLights,
+
+ decodeVideoTexture: material.map != null &&
+ ( material.map is VideoTexture) &&
+ ( ColorManagement.getTransfer( ColorSpace.fromString(material.map!.colorSpace) ) == SRGBTransfer ),
+
+ premultipliedAlpha: material.premultipliedAlpha,
+
+ doubleSided: material.side == DoubleSide,
+ flipSided: material.side == BackSide,
+
+ useDepthPacking: (material.depthPacking ?? 0) >= 0,
+ depthPacking: material.depthPacking ?? 0,
+
+ index0AttributeName: material.index0AttributeName,
+
+ extensionClipCullDistance: material.extensions != null && material.extensions?['clipCullDistance'] == true && extensions.has( 'WEBGL_clip_cull_distance' ),
+ extensionMultiDraw: material.extensions != null && material.extensions?['multiDraw'] == true && extensions.has( 'WEBGL_multi_draw' ),
+
+ rendererExtensionParallelShaderCompile: extensions.has( 'KHR_parallel_shader_compile' ),
+
+ customProgramCacheKey: material.customProgramCacheKey()
+ );
+
+ parameters.vertexUv1s = _activeChannels.contains( 1 );
+ parameters.vertexUv2s = _activeChannels.contains( 2 );
+ parameters.vertexUv3s = _activeChannels.contains( 3 );
+
+ _activeChannels.clear();
+
+ return parameters;
+ }
+
+ String getProgramCacheKey(Parameters parameters) {
+ List array = [];
+
+ if (parameters.shaderID != null) {
+ array.add(parameters.shaderID!);
+ } else {
+ array.add(parameters.customVertexShaderID);
+ array.add(parameters.customFragmentShaderID);
+ }
+
+ if (parameters.defines != null) {
+ for (final name in parameters.defines!.keys) {
+ array.add(name);
+ array.add(parameters.defines![name].toString());
+ }
+ }
+
+ if (parameters is! RawShaderMaterial) {
+ getProgramCacheKeyParameters(array, parameters);
+ getProgramCacheKeyBooleans(array, parameters);
+
+ array.add(renderer.outputEncoding.toString());
+ }
+ array.add(parameters.customProgramCacheKey);
+
+ return array.join();
+ }
+
+ void getProgramCacheKeyParameters(List array, Parameters parameters) {
+ array.add( parameters.precision );
+ array.add( parameters.outputColorSpace );
+ array.add( parameters.envMapMode );
+ array.add( parameters.envMapCubeUVHeight );
+ array.add( parameters.mapUv );
+ array.add( parameters.alphaMapUv );
+ array.add( parameters.lightMapUv );
+ array.add( parameters.aoMapUv );
+ array.add( parameters.bumpMapUv );
+ array.add( parameters.normalMapUv );
+ array.add( parameters.displacementMapUv );
+ array.add( parameters.emissiveMapUv );
+ array.add( parameters.metalnessMapUv );
+ array.add( parameters.roughnessMapUv );
+ array.add( parameters.anisotropyMapUv );
+ array.add( parameters.clearcoatMapUv );
+ array.add( parameters.clearcoatNormalMapUv );
+ array.add( parameters.clearcoatRoughnessMapUv );
+ array.add( parameters.iridescenceMapUv );
+ array.add( parameters.iridescenceThicknessMapUv );
+ array.add( parameters.sheenColorMapUv );
+ array.add( parameters.sheenRoughnessMapUv );
+ array.add( parameters.specularMapUv );
+ array.add( parameters.specularColorMapUv );
+ array.add( parameters.specularIntensityMapUv );
+ array.add( parameters.transmissionMapUv );
+ array.add( parameters.thicknessMapUv );
+ array.add( parameters.combine );
+ array.add( parameters.fogExp2 );
+ array.add( parameters.sizeAttenuation );
+ array.add( parameters.morphTargetsCount );
+ array.add( parameters.morphAttributeCount );
+ array.add( parameters.numDirLights );
+ array.add( parameters.numPointLights );
+ array.add( parameters.numSpotLights );
+ array.add( parameters.numSpotLightMaps );
+ array.add( parameters.numHemiLights );
+ array.add( parameters.numRectAreaLights );
+ array.add( parameters.numDirLightShadows );
+ array.add( parameters.numPointLightShadows );
+ array.add( parameters.numSpotLightShadows );
+ array.add( parameters.numSpotLightShadowsWithMaps );
+ array.add( parameters.numLightProbes );
+ array.add( parameters.shadowMapType );
+ array.add( parameters.toneMapping );
+ array.add( parameters.numClippingPlanes );
+ array.add( parameters.numClipIntersection );
+ array.add( parameters.depthPacking );
+ }
+
+ void getProgramCacheKeyBooleans(List array, Parameters parameters) {
+
+ _programLayers.disableAll();
+
+ if ( parameters.supportsVertexTextures )_programLayers.enable( 0 );
+ if ( parameters.instancing )_programLayers.enable( 1 );
+ if ( parameters.instancingColor )_programLayers.enable( 2 );
+ if ( parameters.instancingMorph )_programLayers.enable( 3 );
+ if ( parameters.matcap )_programLayers.enable( 4 );
+ if ( parameters.envMap )_programLayers.enable( 5 );
+ if ( parameters.normalMapObjectSpace )_programLayers.enable( 6 );
+ if ( parameters.normalMapTangentSpace )_programLayers.enable( 7 );
+ if ( parameters.clearcoat )_programLayers.enable( 8 );
+ if ( parameters.iridescence )_programLayers.enable( 9 );
+ if ( parameters.alphaTest )_programLayers.enable( 10 );
+ if ( parameters.vertexColors )_programLayers.enable( 11 );
+ if ( parameters.vertexAlphas )_programLayers.enable( 12 );
+ if ( parameters.vertexUv1s )_programLayers.enable( 13 );
+ if ( parameters.vertexUv2s )_programLayers.enable( 14 );
+ if ( parameters.vertexUv3s )_programLayers.enable( 15 );
+ if ( parameters.vertexTangents )_programLayers.enable( 16 );
+ if ( parameters.anisotropy )_programLayers.enable( 17 );
+ if ( parameters.alphaHash )_programLayers.enable( 18 );
+ if ( parameters.batching )_programLayers.enable( 19 );
+ if ( parameters.dispersion )_programLayers.enable( 20 );
+ if ( parameters.batchingColor )_programLayers.enable( 21 );
+
+ array.add( _programLayers.mask );
+ _programLayers.disableAll();
+
+ if ( parameters.fog )_programLayers.enable( 0 );
+ if ( parameters.useFog )_programLayers.enable( 1 );
+ if ( parameters.flatShading )_programLayers.enable( 2 );
+ if ( parameters.logarithmicDepthBuffer )_programLayers.enable( 3 );
+ if ( parameters.skinning )_programLayers.enable( 4 );
+ if ( parameters.morphTargets )_programLayers.enable( 5 );
+ if ( parameters.morphNormals )_programLayers.enable( 6 );
+ if ( parameters.morphColors )_programLayers.enable( 7 );
+ if ( parameters.premultipliedAlpha )_programLayers.enable( 8 );
+ if ( parameters.shadowMapEnabled )_programLayers.enable( 9 );
+ if ( parameters.useLegacyLights )_programLayers.enable( 10 );
+ if ( parameters.doubleSided )_programLayers.enable( 11 );
+ if ( parameters.flipSided )_programLayers.enable( 12 );
+ if ( parameters.useDepthPacking )_programLayers.enable( 13 );
+ if ( parameters.dithering )_programLayers.enable( 14 );
+ if ( parameters.transmission )_programLayers.enable( 15 );
+ if ( parameters.sheen )_programLayers.enable( 16 );
+ if ( parameters.opaque )_programLayers.enable( 17 );
+ if ( parameters.pointsUvs )_programLayers.enable( 18 );
+ if ( parameters.decodeVideoTexture )_programLayers.enable( 19 );
+ if ( parameters.decodeVideoTextureEmissive )_programLayers.enable( 20 );
+ if ( parameters.alphaToCoverage )_programLayers.enable( 21 );
+
+ array.add( _programLayers.mask );
+ }
+
+ Map getUniforms(Material material) {
+ String? shaderID = shaderIDs[material.shaderID];
+ Map uniforms;
+
+ if (shaderID != null) {
+ final shader = shaderLib[shaderID];
+ uniforms = cloneUniforms(shader["uniforms"]);
+ } else {
+ uniforms = material.uniforms;
+ }
+
+ return uniforms;
+ }
+
+ AngleProgram? acquireProgram(Parameters parameters, String cacheKey) {
+ AngleProgram? program;
+
+ // Check if code has been already compiled
+ for (int p = 0, pl = programs.length; p < pl; p++) {
+ final preexistingProgram = programs[p];
+
+ if (preexistingProgram.cacheKey == cacheKey) {
+ program = preexistingProgram;
+ ++program.usedTimes;
+
+ break;
+ }
+ }
+
+ if (program == null) {
+ program = AngleProgram(renderer, cacheKey, parameters, bindingStates);
+ programs.add(program);
+ }
+
+ return program;
+ }
+
+ void releaseProgram(AngleProgram program) {
+ if (--program.usedTimes == 0) {
+ // Remove from unordered set
+ final i = programs.indexOf(program);
+ programs[i] = programs[programs.length - 1];
+ programs.removeLast();
+
+ // Free WebGL resources
+ program.destroy();
+ }
+ }
+
+ void releaseShaderCache(Material material) {
+ _customShaders.remove(material);
+ }
+}
diff --git a/packages/three_js_angle_renderer/lib/angle/angle_properties.dart b/packages/three_js_angle_renderer/lib/angle/angle_properties.dart
new file mode 100755
index 00000000..65482afa
--- /dev/null
+++ b/packages/three_js_angle_renderer/lib/angle/angle_properties.dart
@@ -0,0 +1,38 @@
+part of three_webgl;
+
+class AngleProperties {
+ final properties = WeakMap?>();
+
+ bool has( object ) {
+ return properties.has( object );
+ }
+
+ Map get(object) {
+ Map map;
+
+ if (!properties.contains(object)) {
+ map = {};
+ properties[object] = map;
+ }
+ else {
+ map = properties[object]!;
+ }
+
+ return map;
+ }
+
+ void remove(object) {
+ properties.remove(object);
+ }
+
+ void update(object, key, value) {
+ // final m = properties[object]!;
+ // m[key] = value;
+
+ properties.get( object )![ key ] = value;
+ }
+
+ void dispose() {
+ properties.clear();
+ }
+}
diff --git a/packages/three_js_angle_renderer/lib/angle/angle_render_list.dart b/packages/three_js_angle_renderer/lib/angle/angle_render_list.dart
new file mode 100755
index 00000000..5f4c2409
--- /dev/null
+++ b/packages/three_js_angle_renderer/lib/angle/angle_render_list.dart
@@ -0,0 +1,228 @@
+part of three_webgl;
+
+class RenderItem {
+ int id = 0;
+ Object3D? object;
+ BufferGeometry? geometry;
+ Material? material;
+ dynamic program;
+ int groupOrder = 0;
+ int renderOrder = 0;
+ double z = 0;
+ Map? group;
+
+ void dispose(){
+ material?.dispose();
+ object?.dispose();
+ geometry?.dispose();
+ }
+ RenderItem({
+ this.id = 0,
+ this.object,
+ this.geometry,
+ this.material,
+ this.program,
+ this.groupOrder = 0,
+ this.renderOrder = 0,
+ this.z = 0,
+ this.group
+ });
+ RenderItem.fromMap(Map json) {
+ if (json["id"] != null) {
+ id = json["id"];
+ }
+ if (json["object"] != null) {
+ object = json["object"];
+ }
+ if (json["geometry"] != null) {
+ geometry = json["geometry"];
+ }
+ if (json["material"] != null) {
+ material = json["material"];
+ }
+ if (json["program"] != null) {
+ program = json["program"];
+ }
+ if (json["groupOrder"] != null) {
+ groupOrder = json["groupOrder"];
+ }
+
+ if (json["renderOrder"] != null) {
+ renderOrder = json["renderOrder"];
+ }
+ if (json["z"] != null) {
+ z = json["z"];
+ }
+ if (json["group"] != null) {
+ group = json["group"];
+ }
+ }
+}
+
+class AngleRenderList {
+ AngleRenderList();
+
+ Map renderItems = {};
+ int renderItemsIndex = 0;
+
+ List opaque = [];
+ List transmissive = [];
+ List transparent = [];
+
+ final defaultProgram = DefaultProgram();
+
+ void init() {
+ renderItemsIndex = 0;
+ opaque.clear();
+ transmissive.clear();
+ transparent.clear();
+ }
+
+ void dispose(){
+ for(final key in renderItems.keys){
+ renderItems[key]?.dispose();
+ }
+ for(final o in opaque){
+ o.dispose();
+ }
+ for(final t in transmissive){
+ t.dispose();
+ }
+ for(final t in transparent){
+ t.dispose();
+ }
+ }
+
+ RenderItem getNextRenderItem(
+ Object3D object,
+ BufferGeometry? geometry,
+ Material? material,
+ int groupOrder,
+ double z,
+ Map? group
+ ) {
+ RenderItem? renderItem = renderItems[renderItemsIndex];
+
+ if (renderItem == null) {
+ renderItem = RenderItem.fromMap({
+ "id": object.id,
+ "object": object,
+ "geometry": geometry,
+ "material": material,
+ "groupOrder": groupOrder,
+ "renderOrder": object.renderOrder,
+ "z": z,
+ "group": group
+ });
+ }
+ else {
+ renderItem = RenderItem(
+ id: object.id,
+ object: object,
+ geometry: geometry,
+ material: material,
+ groupOrder: groupOrder,
+ renderOrder: object.renderOrder,
+ z: z,
+ group: group
+ );
+ }
+
+ renderItems[renderItemsIndex] = renderItem;
+ renderItemsIndex++;
+
+ return renderItem;
+ }
+
+ void push(Object3D object, BufferGeometry geometry, Material material, int groupOrder, double z, Map? group) {
+ final renderItem = getNextRenderItem(object, geometry, material, groupOrder, z, group);
+
+ if (material.transmission > 0.0) {
+ transmissive.add(renderItem);
+ }
+ else {
+ if (material.transparent) {
+ transparent.add(renderItem);
+ }
+ else {
+ opaque.add(renderItem);
+ }
+ }
+ }
+
+ void unshift(Object3D object, BufferGeometry? geometry, Material? material, int groupOrder, double z, Map? group) {
+ final renderItem = getNextRenderItem(object, geometry, material, groupOrder, z, group);
+
+ if ((material?.transmission ?? 0) > 0.0) {
+ transmissive.insert(0, renderItem);
+ }
+ else {
+ if (material?.transparent == true) {
+ transparent.insert(0, renderItem);
+ }
+ else {
+ opaque.insert(0, renderItem);
+ }
+ }
+ }
+
+ void sort(customOpaqueSort, customTransparentSort) {
+ if (opaque.length > 1) {
+ opaque.sort(customOpaqueSort ?? painterSortStable);
+ }
+
+ if (transmissive.length > 1) {
+ transmissive.sort(customTransparentSort ?? reversePainterSortStable);
+ }
+
+ if (transparent.length > 1) {
+ transparent.sort(customTransparentSort ?? reversePainterSortStable);
+ }
+ }
+
+ void finish() {
+ // Clear references from inactive renderItems in the list
+
+ for (int i = renderItemsIndex, il = renderItems.length; i < il; i++) {
+ final renderItem = renderItems[i]!;
+
+ if (renderItem.id == 0) break;
+
+ renderItem.id = 0;
+ renderItem.object = null;
+ renderItem.geometry = null;
+ renderItem.material = null;
+ renderItem.program = null;
+ renderItem.group = null;
+ }
+ }
+
+ int painterSortStable(RenderItem a, RenderItem b) {
+ if (a.groupOrder != b.groupOrder) {
+ return a.groupOrder - b.groupOrder;
+ } else if (a.renderOrder != b.renderOrder) {
+ return (a.renderOrder - b.renderOrder) > 0 ? 1 : -1;
+ } else if (a.program != b.program) {
+ return a.program.id - b.program.id;
+ } else if (a.material!.id != b.material!.id) {
+ return a.material!.id - b.material!.id;
+ } else if (a.z != b.z) {
+ return (a.z - b.z) > 0 ? 1 : -1;
+ } else {
+ return a.id - b.id;
+ }
+ }
+
+ int reversePainterSortStable(RenderItem a, RenderItem b) {
+ if (a.groupOrder != b.groupOrder) {
+ return a.groupOrder - b.groupOrder;
+ } else if (a.renderOrder != b.renderOrder) {
+ return a.renderOrder - b.renderOrder;
+ } else if (a.z != b.z) {
+ final v = b.z - a.z;
+ return v > 0 ? 1 : -1;
+ } else {
+ return a.id - b.id;
+ }
+ }
+}
diff --git a/packages/three_js_angle_renderer/lib/angle/angle_render_lists.dart b/packages/three_js_angle_renderer/lib/angle/angle_render_lists.dart
new file mode 100755
index 00000000..0245a487
--- /dev/null
+++ b/packages/three_js_angle_renderer/lib/angle/angle_render_lists.dart
@@ -0,0 +1,30 @@
+part of three_webgl;
+
+class AngleRenderLists {
+ AngleRenderLists();
+
+ WeakMap lists = WeakMap();
+
+ AngleRenderList get(scene, renderCallDepth) {
+ final listArray = lists.get( scene );
+ dynamic list;
+
+ if (lists.has(scene) == false) {
+ list = AngleRenderList();
+ lists.add(key: scene, value: [list]);
+ } else {
+ if (renderCallDepth >= listArray.length) {
+ list = AngleRenderList();
+ listArray.add(list);
+ } else {
+ list = listArray[renderCallDepth];
+ }
+ }
+
+ return list;
+ }
+
+ void dispose() {
+ lists.clear();
+ }
+}
diff --git a/packages/three_js_angle_renderer/lib/angle/angle_render_states.dart b/packages/three_js_angle_renderer/lib/angle/angle_render_states.dart
new file mode 100755
index 00000000..453147d2
--- /dev/null
+++ b/packages/three_js_angle_renderer/lib/angle/angle_render_states.dart
@@ -0,0 +1,92 @@
+part of three_webgl;
+
+class AngleRenderState {
+ bool _didDispose = false;
+ late AngleLights lights;
+ AngleExtensions extensions;
+ List lightsArray = [];
+ List shadowsArray = [];
+ Map transmissionRenderTarget = {};
+ late RenderState _renderState;
+
+ AngleRenderState(this.extensions) {
+ lights = AngleLights(extensions);
+ _renderState = RenderState(lights, lightsArray, shadowsArray, null, {});
+ }
+
+ RenderState get state {
+ return _renderState;
+ }
+
+ void dispose(){
+ if(_didDispose) return;
+ _didDispose = true;
+ lightsArray.clear();
+ shadowsArray.clear();
+ lights.dispose();
+ lights.dispose();
+ extensions.dispose();
+ }
+
+ void init(Camera camera) {
+ state.camera = camera;
+
+ lightsArray.length = 0;
+ shadowsArray.length = 0;
+ }
+
+ void pushLight(Light light) {
+ lightsArray.add(light);
+ }
+
+ void pushShadow(Light shadowLight) {
+ shadowsArray.add(shadowLight);
+ }
+
+ void setupLights([bool? physicallyCorrectLights]) {
+ lights.setup(lightsArray, physicallyCorrectLights);
+ }
+
+ void setupLightsView(Camera camera) {
+ lights.setupView(lightsArray, camera);
+ }
+}
+
+class AngleRenderStates {
+ AngleExtensions extensions;
+ WeakMap renderStates = WeakMap();
+
+ AngleRenderStates(this.extensions);
+
+ AngleRenderState get(Object3D scene, {int renderCallDepth = 0}) {
+ AngleRenderState renderState;
+
+ if (!renderStates.has(scene)) {
+ renderState = AngleRenderState(extensions);
+ renderStates.add(key: scene, value: [renderState]);
+ } else {
+ if (renderCallDepth >= renderStates.get(scene).length) {
+ renderState = AngleRenderState(extensions);
+ renderStates.get(scene).add(renderState);
+ } else {
+ renderState = renderStates.get(scene)[renderCallDepth];
+ }
+ }
+
+ return renderState;
+ }
+
+ void dispose() {
+ renderStates.clear();
+ }
+}
+
+class RenderState {
+ AngleLights lights;
+ List lightsArray;
+ List shadowsArray;
+ Camera? camera;
+ Map transmissionRenderTarget;
+
+ RenderState(this.lights, this.lightsArray, this.shadowsArray, this.camera, this.transmissionRenderTarget);
+}
diff --git a/packages/three_js_angle_renderer/lib/angle/angle_shader.dart b/packages/three_js_angle_renderer/lib/angle/angle_shader.dart
new file mode 100755
index 00000000..a5d4c30c
--- /dev/null
+++ b/packages/three_js_angle_renderer/lib/angle/angle_shader.dart
@@ -0,0 +1,19 @@
+part of three_webgl;
+
+class AngleShader {
+ RenderingContext gl;
+ dynamic shader;
+ String content;
+
+ AngleShader(this.gl, int type, this.content) {
+ shader = gl.createShader(type);
+
+ gl.shaderSource(shader, content);
+ gl.compileShader(shader);
+
+ final status = gl.getShaderParameter(shader, WebGL.COMPILE_STATUS);
+ if (!status) {
+ throw (" WebGLShader comile error.... _status: $content $status ${gl.getShaderInfoLog(shader)} ");
+ }
+ }
+}
diff --git a/packages/three_js_angle_renderer/lib/angle/angle_shader_cache.dart b/packages/three_js_angle_renderer/lib/angle/angle_shader_cache.dart
new file mode 100755
index 00000000..0a54f14d
--- /dev/null
+++ b/packages/three_js_angle_renderer/lib/angle/angle_shader_cache.dart
@@ -0,0 +1,92 @@
+part of three_webgl;
+
+int _id = 0;
+
+class AngleShaderCache {
+ final shaderCache = {};
+ final materialCache = {};
+
+ AngleShaderCache();
+
+ AngleShaderCache update(Material material) {
+ final vertexShader = material.vertexShader;
+ final fragmentShader = material.fragmentShader;
+
+ final vertexShaderStage = _getShaderStage(vertexShader!);
+ final fragmentShaderStage = _getShaderStage(fragmentShader!);
+
+ final materialShaders = _getShaderCacheForMaterial(material);
+
+ if (materialShaders.contains(vertexShaderStage) == false) {
+ materialShaders.add(vertexShaderStage);
+ vertexShaderStage.usedTimes++;
+ }
+
+ if (materialShaders.contains(fragmentShaderStage) == false) {
+ materialShaders.add(fragmentShaderStage);
+ fragmentShaderStage.usedTimes++;
+ }
+
+ return this;
+ }
+
+ AngleShaderCache remove(Material material) {
+ final materialShaders = materialCache[material];
+ if(materialShaders != null){
+ for (final shaderStage in materialShaders) {
+ shaderStage.usedTimes--;
+
+ if (shaderStage.usedTimes == 0) shaderCache.remove(shaderStage.code);
+ }
+ }
+
+ materialCache.remove(material);
+
+ return this;
+ }
+
+ getVertexShaderID(Material material) {
+ return _getShaderStage(material.vertexShader!).id;
+ }
+
+ getFragmentShaderID(Material material) {
+ return _getShaderStage(material.fragmentShader!).id;
+ }
+
+ void dispose() {
+ shaderCache.clear();
+ materialCache.clear();
+ }
+
+ _getShaderCacheForMaterial(Material material) {
+ final cache = materialCache;
+
+ if (cache.containsKey(material) == false) {
+ cache[material] = [];
+ }
+
+ return cache[material];
+ }
+
+ _getShaderStage(String code) {
+ final cache = shaderCache;
+
+ if (cache.containsKey(code) == false) {
+ final stage = WebGLShaderStage(code);
+ cache[code] = stage;
+ }
+
+ return cache[code];
+ }
+}
+
+class WebGLShaderStage {
+ late int id;
+ late int usedTimes;
+ late String code;
+
+ WebGLShaderStage(this.code) {
+ id = _id++;
+ usedTimes = 0;
+ }
+}
diff --git a/packages/three_js_angle_renderer/lib/angle/angle_shadow_map.dart b/packages/three_js_angle_renderer/lib/angle/angle_shadow_map.dart
new file mode 100755
index 00000000..3887576e
--- /dev/null
+++ b/packages/three_js_angle_renderer/lib/angle/angle_shadow_map.dart
@@ -0,0 +1,327 @@
+part of three_webgl;
+
+class AngleShadowMap extends ShadowMap {
+ bool _didDispose = false;
+ Frustum _frustum = Frustum();
+ final _shadowMapSize = Vector2.zero();
+ final _viewportSize = Vector2.zero();
+ final _viewport = Vector4.identity();
+
+ late MeshDepthMaterial _depthMaterial;
+ late MeshDistanceMaterial _distanceMaterial;
+
+ final _materialCache = {};
+
+ final AngleRenderer _renderer;
+ final AngleObjects _objects;
+ final AngleCapabilities _capabilities;
+ late int _maxTextureSize;
+
+ late int _previousType;
+
+ AngleShadowMap(this._renderer, this._objects, this._capabilities) {
+ _previousType = type;
+ _maxTextureSize = _capabilities.maxTextureSize;
+
+ _depthMaterial = MeshDepthMaterial.fromMap({"depthPacking": RGBADepthPacking});
+ _distanceMaterial = MeshDistanceMaterial(null);
+
+ shadowMaterialVertical = ShaderMaterial.fromMap({
+ "defines": {"VSM_SAMPLES": 8},
+ "uniforms": {
+ "shadow_pass": {"value": null},
+ "resolution": {"value": Vector2.zero()},
+ "radius": {"value": 4.0}
+ },
+ "vertexShader": vsmVert,
+ "fragmentShader": vsmFrag
+ });
+
+ final float32List = Float32List.fromList([-1.0, -1.0, 0.5, 3.0, -1.0, 0.5, -1.0, 3.0, 0.5]);
+
+ fullScreenTri.setAttributeFromString('position', Float32BufferAttribute.fromList(float32List, 3, false));
+
+ fullScreenMesh = Mesh(fullScreenTri, shadowMaterialVertical);
+
+ shadowMaterialHorizontal = shadowMaterialVertical.clone();
+ shadowMaterialHorizontal.defines!["HORIZONTAL_PASS"] = 1;
+
+ scope = this;
+ }
+
+ void dispose(){
+ if(_didDispose) return;
+ _didDispose = true;
+ fullScreenMesh.dispose();
+ fullScreenTri.dispose();
+
+ _depthMaterial.dispose();
+ _distanceMaterial.dispose();
+
+ shadowMaterialVertical.dispose();
+ shadowMaterialHorizontal.dispose();
+
+ _frustum.dispose();
+ shadowSide.clear();
+
+ scope.dispose();
+ _renderer.dispose();
+ _objects.dispose();
+ _capabilities.dispose();
+ }
+
+ void render(List lights, Object3D scene, Camera camera) {
+ if (!scope.enabled) return;
+ if (!scope.autoUpdate && !scope.needsUpdate) return;
+
+ if (lights.isEmpty) return;
+
+ final currentRenderTarget = _renderer.getRenderTarget();
+ final activeCubeFace = _renderer.getActiveCubeFace();
+ final activeMipmapLevel = _renderer.getActiveMipmapLevel();
+
+ final state = _renderer.state;
+
+ // Set GL state for depth map.
+ state.setBlending(NoBlending);
+ state.buffers["color"].setClear(1.0, 1.0, 1.0, 1.0, false);
+ state.buffers["depth"].setTest(true);
+ state.setScissorTest(false);
+
+ final toVSM = ( _previousType != VSMShadowMap && type == VSMShadowMap );
+ final fromVSM = ( _previousType == VSMShadowMap && type != VSMShadowMap );
+
+ // render depth map
+
+ for (int i = 0, il = lights.length; i < il; i++) {
+ final light = lights[i];
+ final shadow = light.shadow;
+
+ if (shadow == null) {
+ continue;
+ }
+
+ if (!shadow.autoUpdate && !shadow.needsUpdate) continue;
+
+ _shadowMapSize.setFrom(shadow.mapSize);
+
+ final shadowFrameExtents = shadow.getFrameExtents();
+ _shadowMapSize.multiply(shadowFrameExtents);
+ _viewportSize.setFrom(shadow.mapSize);
+
+ if (_shadowMapSize.x > _maxTextureSize || _shadowMapSize.y > _maxTextureSize) {
+ if (_shadowMapSize.x > _maxTextureSize) {
+ _viewportSize.x = (_maxTextureSize / shadowFrameExtents.x).floorToDouble();
+ _shadowMapSize.x = _viewportSize.x * shadowFrameExtents.x;
+ shadow.mapSize.x = _viewportSize.x;
+ }
+
+ if (_shadowMapSize.y > _maxTextureSize) {
+ _viewportSize.y = (_maxTextureSize / shadowFrameExtents.y).floorToDouble();
+ _shadowMapSize.y = _viewportSize.y * shadowFrameExtents.y;
+ shadow.mapSize.y = _viewportSize.y;
+ }
+ }
+
+ if (shadow.map == null || toVSM || fromVSM && shadow is! PointLightShadow && type == VSMShadowMap) {
+ final Map pars = (type != VSMShadowMap ) ? { 'minFilter': NearestFilter, 'magFilter': NearestFilter } : {};
+
+ if ( shadow.map != null ) {
+ shadow.map?.dispose();
+ }
+
+ shadow.map = RenderTarget(_shadowMapSize.x.toInt(), _shadowMapSize.y.toInt(), RenderTargetOptions(pars));
+ shadow.map!.texture.name = '${light.name}.shadowMap';
+
+ shadow.camera!.updateProjectionMatrix();
+ }
+
+ _renderer.setRenderTarget(shadow.map);
+ _renderer.clear();
+
+ final viewportCount = shadow.getViewportCount();
+
+ for (int vp = 0; vp < viewportCount; vp++) {
+ final viewport = shadow.getViewport(vp);
+ _viewport.setValues(_viewportSize.x * viewport.x, _viewportSize.y * viewport.y, _viewportSize.x * viewport.z, _viewportSize.y * viewport.w);
+ state.viewport(_viewport);
+ shadow.updateMatrices(light, viewportIndex: vp);
+ _frustum = shadow.getFrustum();
+ renderObject(scene, camera, shadow.camera!, light, type);
+ }
+
+ // do blur pass for VSM
+
+ if (shadow is! PointLightShadow && type == VSMShadowMap) {
+ vSMPass(shadow, camera);
+ }
+
+ shadow.needsUpdate = false;
+ }
+ _previousType = type;
+
+ scope.needsUpdate = false;
+ _renderer.setRenderTarget(currentRenderTarget, activeCubeFace, activeMipmapLevel);
+ }
+
+ void vSMPass(LightShadow shadow, Camera camera) {
+ final geometry = _objects.update(fullScreenMesh);
+
+ if (shadowMaterialVertical.defines!["VSM_SAMPLES"] != shadow.blurSamples) {
+ shadowMaterialVertical.defines!["VSM_SAMPLES"] = shadow.blurSamples;
+ shadowMaterialHorizontal.defines!["VSM_SAMPLES"] = shadow.blurSamples;
+
+ shadowMaterialVertical.needsUpdate = true;
+ shadowMaterialHorizontal.needsUpdate = true;
+ }
+
+ shadow.mapPass ??= RenderTarget( _shadowMapSize.x.toInt(), _shadowMapSize.y.toInt() );
+
+ // vertical pass
+
+ shadowMaterialVertical.uniforms["shadow_pass"]['value'] = shadow.map!.texture;
+ shadowMaterialVertical.uniforms["resolution"]['value'] = shadow.mapSize;
+ shadowMaterialVertical.uniforms["radius"]['value'] = shadow.radius;
+
+ _renderer.setRenderTarget(shadow.mapPass);
+ _renderer.clear();
+ _renderer.renderBufferDirect(camera, null, geometry, shadowMaterialVertical, fullScreenMesh, null);
+
+ // horizontal pass
+
+ shadowMaterialHorizontal.uniforms["shadow_pass"]['value'] = shadow.mapPass!.texture;
+ shadowMaterialHorizontal.uniforms["resolution"]['value'] = shadow.mapSize;
+ shadowMaterialHorizontal.uniforms["radius"]['value'] = shadow.radius;
+
+ _renderer.setRenderTarget(shadow.map);
+ _renderer.clear();
+ _renderer.renderBufferDirect(camera, null, geometry, shadowMaterialHorizontal, fullScreenMesh, null);
+ }
+
+ Material getDepthMaterial(
+ Object3D object,
+ Material material,
+ Light light,
+ double shadowCameraNear,
+ double shadowCameraFar,
+ int type
+ ) {
+ Material? result;
+
+ final customMaterial = light is PointLight ? object.customDistanceMaterial : object.customDepthMaterial;
+
+ if (customMaterial != null) {
+ result = customMaterial;
+ } else {
+ result = light is PointLight ? _distanceMaterial : _depthMaterial;
+
+ if ((
+ _renderer.localClippingEnabled &&
+ material.clipShadows == true &&
+ material.clippingPlanes!.isNotEmpty
+ ) ||
+ (material.displacementMap != null && material.displacementScale != 0 ) ||
+ ( material.alphaMap != null && material.alphaTest > 0 ) ||
+ (material.map != null && material.alphaTest > 0)
+ ) {
+ // in this case we need a unique material instance reflecting the
+ // appropriate state
+
+ final keyA = result.uuid;
+ final keyB = material.uuid;
+
+ Map? materialsForVariant = _materialCache[keyA];
+
+ if (materialsForVariant == null) {
+ materialsForVariant = {};
+ _materialCache[keyA] = materialsForVariant;
+ }
+
+ Material? cachedMaterial = materialsForVariant[keyB];
+
+ if (cachedMaterial == null) {
+ cachedMaterial = result.clone();
+ materialsForVariant[keyB] = cachedMaterial;
+ }
+
+ result = cachedMaterial;
+ }
+ }
+
+ result.visible = material.visible;
+ result.wireframe = material.wireframe;
+
+ if (type == VSMShadowMap) {
+ result.side = (material.shadowSide != null) ? material.shadowSide! : material.side;
+ }
+ else {
+ result.side = (material.shadowSide != null) ? material.shadowSide! : shadowSide[material.side]!;
+ }
+
+ result.alphaMap = material.alphaMap;
+ result.alphaTest = material.alphaTest;
+ result.map = material.map;
+
+ result.clipShadows = material.clipShadows;
+ result.clippingPlanes = material.clippingPlanes;
+ result.clipIntersection = material.clipIntersection;
+
+ result.wireframeLinewidth = material.wireframeLinewidth;
+ result.linewidth = material.linewidth;
+
+ if (light is PointLight == true && result is MeshDistanceMaterial) {
+ // result.referencePosition.setFromMatrixPosition(light.matrixWorld);
+ // result.nearDistance = shadowCameraNear;
+ // result.farDistance = shadowCameraFar;
+
+ final materialProperties = _renderer.properties.get( result );
+ materialProperties['light'] = light;
+ }
+
+ return result;
+ }
+
+ void renderObject(Object3D object, Camera camera, Camera shadowCamera, Light light, int type) {
+ if (object.visible == false) return;
+
+ final visible = object.layers.test(camera.layers);
+
+ if (visible && (object is Mesh || object is Line || object is Points)) {
+ if ((object.castShadow || (object.receiveShadow && type == VSMShadowMap)) &&
+ (!object.frustumCulled || _frustum.intersectsObject(object))) {
+ object.modelViewMatrix.multiply2(shadowCamera.matrixWorldInverse, object.matrixWorld);
+
+ final geometry = _objects.update(object);
+ final material = object.material;
+
+ if (material is GroupMaterial) {
+ final groups = geometry.groups;
+
+ for (int k = 0, kl = groups.length; k < kl; k++) {
+ final group = groups[k];
+ final groupMaterial = material.children[group["materialIndex"]];
+
+ if (groupMaterial.visible) {//groupMaterial != null &&
+ final depthMaterial = getDepthMaterial(object, groupMaterial, light, shadowCamera.near, shadowCamera.far, type);
+ object.onBeforeShadow(renderer: _renderer, scene: object, camera: camera, shadowCamera: shadowCamera, geometry: geometry, material: depthMaterial, group: group);
+ _renderer.renderBufferDirect(shadowCamera, null, geometry, depthMaterial, object, group);
+ object.onAfterShadow(renderer: _renderer, scene: object, camera: camera, shadowCamera: shadowCamera, geometry: geometry, material: depthMaterial, group: group);
+ }
+ }
+ }
+ else if (material != null && material.visible) {
+ final depthMaterial = getDepthMaterial(object, material, light, shadowCamera.near, shadowCamera.far, type);
+ object.onBeforeShadow(renderer: _renderer, scene: object, camera: camera, shadowCamera: shadowCamera, geometry: geometry, material: depthMaterial);
+ _renderer.renderBufferDirect(shadowCamera, null, geometry, depthMaterial, object, null);
+ object.onAfterShadow(renderer: _renderer, scene: object, camera: camera, shadowCamera: shadowCamera, geometry: geometry, material: depthMaterial);
+ }
+ }
+ }
+
+ final children = object.children;
+
+ for (int i = 0, l = children.length; i < l; i++) {
+ renderObject(children[i], camera, shadowCamera, light, type);
+ }
+ }
+}
diff --git a/packages/three_js_angle_renderer/lib/angle/angle_state.dart b/packages/three_js_angle_renderer/lib/angle/angle_state.dart
new file mode 100755
index 00000000..936fa1f4
--- /dev/null
+++ b/packages/three_js_angle_renderer/lib/angle/angle_state.dart
@@ -0,0 +1,1018 @@
+part of three_webgl;
+
+class AngleState extends State {
+ bool _didDispose = false;
+ RenderingContext gl;
+
+ late ColorBuffer colorBuffer;
+ late DepthBuffer depthBuffer;
+ late StencilBuffer stencilBuffer;
+
+ late int maxTextures;
+
+ final emptyTextures = {};
+ late Map equationToGL;
+ late Map factorToGL;
+
+ @override
+ Map get buffers => {"color": colorBuffer, "depth": depthBuffer, "stencil": stencilBuffer};
+ Map enabledCapabilities = {};
+
+ Framebuffer? xrFramebuffer;
+ Map currentBoundFramebuffers = {};
+ final uboBindings = WeakMap();
+ final uboProgramMap = WeakMap();
+ WeakMap currentDrawbuffers = WeakMap();
+ List defaultDrawbuffers = [];
+
+ dynamic currentProgram;
+
+ bool currentBlendingEnabled = false;
+
+ int? currentBlending;
+ int? currentBlendEquation;
+ int? currentBlendSrc;
+ int? currentBlendDst;
+ int? currentBlendEquationAlpha;
+ int? currentBlendSrcAlpha;
+ int? currentBlendDstAlpha;
+ bool? currentPremultipledAlpha;
+ Color currentBlendColor = Color( 0, 0, 0 );
+ double currentBlendAlpha = 0;
+
+ bool? currentFlipSided = false;
+ int? currentCullFace;
+
+ double? currentLineWidth;
+
+ double? currentPolygonOffsetFactor;
+ double? currentPolygonOffsetUnits;
+
+ bool lineWidthAvailable = true;
+
+ int? currentTextureSlot;
+ Map currentBoundTextures = {};
+
+ late Vector4 currentScissor;
+ late Vector4 currentViewport;
+
+ dynamic scissorParam;
+ dynamic viewportParam;
+ AngleExtensions extensions;
+
+ AngleState(this.gl, this.extensions) {
+ colorBuffer = ColorBuffer(gl);
+ depthBuffer = DepthBuffer(gl,extensions);
+ stencilBuffer = StencilBuffer(gl);
+
+ colorBuffer.enable = enable;
+ colorBuffer.disable = disable;
+
+ depthBuffer.enable = enable;
+ depthBuffer.disable = disable;
+
+ stencilBuffer.enable = enable;
+ stencilBuffer.disable = disable;
+
+ maxTextures = gl.getParameter(WebGL.MAX_COMBINED_TEXTURE_IMAGE_UNITS);
+ emptyTextures[WebGL.TEXTURE_2D] = createTexture(WebGL.TEXTURE_2D, WebGL.TEXTURE_2D, 1);
+ emptyTextures[WebGL.TEXTURE_CUBE_MAP] = createTexture(WebGL.TEXTURE_CUBE_MAP, WebGL.TEXTURE_CUBE_MAP_POSITIVE_X, 6);
+
+ colorBuffer.setClear(0, 0, 0, 1, false);
+ depthBuffer.setClear(1);
+ stencilBuffer.setClear(0);
+
+ enable(WebGL.DEPTH_TEST);
+ depthBuffer.setFunc(LessEqualDepth);
+
+ setFlipSided(false);
+ setCullFace(CullFaceBack);
+ enable(WebGL.CULL_FACE);
+
+ setBlending(NoBlending, null, null, null, null, null, null, false);
+
+ equationToGL = {
+ AddEquation: WebGL.FUNC_ADD,
+ SubtractEquation: WebGL.FUNC_SUBTRACT,
+ ReverseSubtractEquation: WebGL.FUNC_REVERSE_SUBTRACT
+ };
+
+ equationToGL[MinEquation] = WebGL.MIN;
+ equationToGL[MaxEquation] = WebGL.MAX;
+
+ factorToGL = {
+ ZeroFactor: WebGL.ZERO,
+ OneFactor: WebGL.ONE,
+ SrcColorFactor: WebGL.SRC_COLOR,
+ SrcAlphaFactor: WebGL.SRC_ALPHA,
+ SrcAlphaSaturateFactor: WebGL.SRC_ALPHA_SATURATE,
+ DstColorFactor: WebGL.DST_COLOR,
+ DstAlphaFactor: WebGL.DST_ALPHA,
+ OneMinusSrcColorFactor: WebGL.ONE_MINUS_SRC_COLOR,
+ OneMinusSrcAlphaFactor: WebGL.ONE_MINUS_SRC_ALPHA,
+ OneMinusDstColorFactor: WebGL.ONE_MINUS_DST_COLOR,
+ OneMinusDstAlphaFactor: WebGL.ONE_MINUS_DST_ALPHA
+ };
+
+ scissorParam = gl.getParameter(WebGL.SCISSOR_BOX);
+ viewportParam = gl.getParameter(WebGL.VIEWPORT);
+
+ // currentScissor = new Vector4.identity().fromArray( scissorParam );
+ // currentViewport = new Vector4.identity().fromArray( viewportParam );
+
+ currentScissor = Vector4.identity();
+ currentViewport = Vector4.identity();
+ }
+
+ final data = Uint8List(4);
+
+ WebGLTexture createTexture(int type, int target, int count) {
+ // 4 is required to match default unpack alignment of 4.
+ //
+ final texture = gl.createTexture();
+
+ gl.bindTexture(type, texture);
+ gl.texParameteri(type, WebGL.TEXTURE_MIN_FILTER, WebGL.NEAREST);
+ gl.texParameteri(type, WebGL.TEXTURE_MAG_FILTER, WebGL.NEAREST);
+
+ for (int i = 0; i < count; i++) {
+ gl.texImage2D(target + i, 0, WebGL.RGBA, 1, 1, 0, WebGL.RGBA, WebGL.UNSIGNED_BYTE, data);
+ }
+
+ //data.dispose();
+
+ return texture;
+ }
+
+ @override
+ void enable(id) {
+ if (enabledCapabilities[id] != true) {
+ gl.enable(id);
+ enabledCapabilities[id] = true;
+ }
+ }
+
+ @override
+ void disable(id) {
+ if (enabledCapabilities[id] != false) {
+ gl.disable(id);
+ enabledCapabilities[id] = false;
+ }
+ }
+
+ void bindXRFramebuffer(Framebuffer? framebuffer) {
+ if (framebuffer != xrFramebuffer) {
+ gl.bindFramebuffer(WebGL.FRAMEBUFFER, framebuffer);
+ xrFramebuffer = framebuffer;
+ }
+ }
+
+ bool bindFramebuffer(target, Framebuffer? framebuffer) {
+ if (framebuffer == null && xrFramebuffer != null) {
+ framebuffer = xrFramebuffer;
+ } // use active XR framebuffer if available
+
+ if (currentBoundFramebuffers[target] != framebuffer) {
+ gl.bindFramebuffer(target, framebuffer);
+
+ currentBoundFramebuffers[target] = framebuffer;
+
+ // gl.DRAW_FRAMEBUFFER is equivalent to gl.FRAMEBUFFER
+ if (target == WebGL.DRAW_FRAMEBUFFER) {
+ currentBoundFramebuffers[WebGL.FRAMEBUFFER] = framebuffer;
+ }
+
+ if (target == WebGL.FRAMEBUFFER) {
+ currentBoundFramebuffers[WebGL.DRAW_FRAMEBUFFER] = framebuffer;
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ void drawBuffers(RenderTarget? renderTarget, Framebuffer? framebuffer) {
+ List? drawBuffers = defaultDrawbuffers;
+
+ bool needsUpdate = false;
+
+ if (renderTarget != null) {
+ drawBuffers = currentDrawbuffers.get(framebuffer);
+
+ if (drawBuffers == null) {
+ drawBuffers = [];
+ currentDrawbuffers.set(framebuffer, drawBuffers);
+ }
+
+ final textures = renderTarget.textures;
+
+ if (drawBuffers.length != textures.length || (drawBuffers.isNotEmpty && drawBuffers[0] != WebGL.COLOR_ATTACHMENT0)) {
+ for (int i = 0, il = textures.length; i < il; i++) {
+ if(drawBuffers.length <= i){
+ drawBuffers.add(WebGL.COLOR_ATTACHMENT0 + i);
+ }else{
+ drawBuffers[i] = WebGL.COLOR_ATTACHMENT0 + i;
+ }
+ }
+
+ drawBuffers.length = textures.length;
+
+ needsUpdate = true;
+ }
+ }
+ else {
+ if (drawBuffers.isEmpty || drawBuffers[0] != WebGL.BACK) {
+ if (drawBuffers.isEmpty) {
+ drawBuffers.add(WebGL.BACK);
+ } else {
+ drawBuffers[0] = WebGL.BACK;
+ }
+
+ drawBuffers.length = 1;
+ needsUpdate = true;
+ }
+ }
+
+ if (needsUpdate) {
+ Uint32List buf = Uint32List.fromList(List.from(drawBuffers));
+ gl.drawBuffers(buf);
+ buf.dispose();
+ }
+ }
+
+ bool useProgram(Program? program) {
+ if (currentProgram != program) {
+ gl.useProgram(program);
+ currentProgram = program;
+ return true;
+ }
+
+ return false;
+ }
+
+ void setBlending(int blending,
+ [int? blendEquation,
+ int? blendSrc,
+ int? blendDst,
+ int? blendEquationAlpha,
+ int? blendSrcAlpha,
+ int? blendDstAlpha,
+ bool premultipliedAlpha = false]) {
+ if (blending == NoBlending) {
+ if (currentBlendingEnabled) {
+ disable(WebGL.BLEND);
+ currentBlendingEnabled = false;
+ }
+
+ return;
+ }
+
+ if (!currentBlendingEnabled) {
+ enable(WebGL.BLEND);
+ currentBlendingEnabled = true;
+ }
+
+ if (blending != CustomBlending) {
+ if (blending != currentBlending || premultipliedAlpha != currentPremultipledAlpha) {
+ if (currentBlendEquation != AddEquation || currentBlendEquationAlpha != AddEquation) {
+ gl.blendEquation(WebGL.FUNC_ADD);
+
+ currentBlendEquation = AddEquation;
+ currentBlendEquationAlpha = AddEquation;
+ }
+
+ if (premultipliedAlpha) {
+ switch (blending) {
+ case NormalBlending:
+ gl.blendFuncSeparate(WebGL.ONE, WebGL.ONE_MINUS_SRC_ALPHA, WebGL.ONE, WebGL.ONE_MINUS_SRC_ALPHA);
+ break;
+
+ case AdditiveBlending:
+ gl.blendFunc(WebGL.ONE, WebGL.ONE);
+ break;
+
+ case SubtractiveBlending:
+ gl.blendFuncSeparate(WebGL.ZERO, WebGL.ONE_MINUS_SRC_COLOR, WebGL.ZERO, WebGL.ONE);
+ break;
+
+ case MultiplyBlending:
+ gl.blendFuncSeparate(WebGL.ZERO, WebGL.SRC_COLOR, WebGL.ZERO, WebGL.SRC_ALPHA);
+ break;
+
+ default:
+ console.error('WebGLState: Invalid blending: $blending');
+ break;
+ }
+ }
+ else {
+ switch (blending) {
+ case NormalBlending:
+ gl.blendFuncSeparate(WebGL.SRC_ALPHA, WebGL.ONE_MINUS_SRC_ALPHA, WebGL.ONE, WebGL.ONE_MINUS_SRC_ALPHA);
+ break;
+
+ case AdditiveBlending:
+ gl.blendFunc(WebGL.SRC_ALPHA, WebGL.ONE);
+ break;
+
+ case SubtractiveBlending:
+ gl.blendFuncSeparate(WebGL.ZERO, WebGL.ONE_MINUS_SRC_COLOR, WebGL.ZERO, WebGL.ONE);
+ break;
+
+ case MultiplyBlending:
+ gl.blendFunc(WebGL.ZERO, WebGL.SRC_COLOR);
+ break;
+
+ default:
+ console.error('WebGLState: Invalid blending: $blending');
+ break;
+ }
+ }
+
+ currentBlendSrc = null;
+ currentBlendDst = null;
+ currentBlendSrcAlpha = null;
+ currentBlendDstAlpha = null;
+ currentBlendColor.setValues( 0, 0, 0 );
+ currentBlendAlpha = 0;
+
+ currentBlending = blending;
+ currentPremultipledAlpha = premultipliedAlpha;
+ }
+
+ return;
+ }
+
+ blendEquationAlpha = blendEquationAlpha ?? blendEquation;
+ blendSrcAlpha = blendSrcAlpha ?? blendSrc;
+ blendDstAlpha = blendDstAlpha ?? blendDst;
+
+ if (blendEquation != currentBlendEquation || blendEquationAlpha != currentBlendEquationAlpha) {
+ gl.blendEquationSeparate(equationToGL[blendEquation]!, equationToGL[blendEquationAlpha]!);
+
+ currentBlendEquation = blendEquation;
+ currentBlendEquationAlpha = blendEquationAlpha;
+ }
+
+ if (blendSrc != currentBlendSrc ||
+ blendDst != currentBlendDst ||
+ blendSrcAlpha != currentBlendSrcAlpha ||
+ blendDstAlpha != currentBlendDstAlpha) {
+ gl.blendFuncSeparate(factorToGL[blendSrc]!, factorToGL[blendDst]!, factorToGL[blendSrcAlpha]!, factorToGL[blendDstAlpha]!);
+
+ currentBlendSrc = blendSrc;
+ currentBlendDst = blendDst;
+ currentBlendSrcAlpha = blendSrcAlpha;
+ currentBlendDstAlpha = blendDstAlpha;
+ }
+
+ currentBlending = blending;
+ currentPremultipledAlpha = null;
+ }
+
+ void setMaterial(Material material, bool frontFaceCW) {
+ material.side == DoubleSide ? disable(WebGL.CULL_FACE) : enable(WebGL.CULL_FACE);
+
+ bool flipSided = (material.side == BackSide);
+ if (frontFaceCW) flipSided = !flipSided;
+
+ setFlipSided(flipSided);
+
+ (material.blending == NormalBlending && material.transparent == false)
+ ? setBlending(NoBlending)
+ : setBlending(material.blending, material.blendEquation, material.blendSrc, material.blendDst,material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha);
+
+ depthBuffer.setFunc(material.depthFunc);
+ depthBuffer.setTest(material.depthTest);
+ depthBuffer.setMask(material.depthWrite);
+ colorBuffer.setMask(material.colorWrite);
+
+ final stencilWrite = material.stencilWrite;
+ stencilBuffer.setTest(stencilWrite);
+ if (stencilWrite) {
+ stencilBuffer.setMask(material.stencilWriteMask);
+ stencilBuffer.setFunc(material.stencilFunc, material.stencilRef, material.stencilFuncMask);
+ stencilBuffer.setOp(material.stencilFail, material.stencilZFail, material.stencilZPass);
+ }
+
+ setPolygonOffset(material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits);
+
+ material.alphaToCoverage == true ? enable(WebGL.SAMPLE_ALPHA_TO_COVERAGE) : disable(WebGL.SAMPLE_ALPHA_TO_COVERAGE);
+ }
+
+ //
+
+ void setFlipSided(bool flipSided) {
+ if (currentFlipSided != flipSided) {
+ if (flipSided) {
+ gl.frontFace(WebGL.CW);
+ }
+ else {
+ gl.frontFace(WebGL.CCW);
+ }
+
+ currentFlipSided = flipSided;
+ }
+ }
+
+ void setCullFace(int cullFace) {
+ if (cullFace != CullFaceNone) {
+ enable(WebGL.CULL_FACE);
+
+ if (cullFace != currentCullFace) {
+ if (cullFace == CullFaceBack) {
+ gl.cullFace(WebGL.BACK);
+ } else if (cullFace == CullFaceFront) {
+ gl.cullFace(WebGL.FRONT);
+ } else {
+ gl.cullFace(WebGL.FRONT_AND_BACK);
+ }
+ }
+ } else {
+ disable(WebGL.CULL_FACE);
+ }
+
+ currentCullFace = cullFace;
+ }
+
+ void setLineWidth(width) {
+ if (width != currentLineWidth) {
+ if (lineWidthAvailable) gl.lineWidth(width);
+
+ currentLineWidth = width;
+ }
+ }
+
+ void setPolygonOffset(bool polygonOffset, [double? factor, double? units]) {
+ if (polygonOffset) {
+ enable(WebGL.POLYGON_OFFSET_FILL);
+
+ if (currentPolygonOffsetFactor != factor || currentPolygonOffsetUnits != units) {
+ gl.polygonOffset(factor!, units!);
+
+ currentPolygonOffsetFactor = factor;
+ currentPolygonOffsetUnits = units;
+ }
+ } else {
+ disable(WebGL.POLYGON_OFFSET_FILL);
+ }
+ }
+
+ void setScissorTest(bool scissorTest) {
+ if (scissorTest) {
+ enable(WebGL.SCISSOR_TEST);
+ } else {
+ disable(WebGL.SCISSOR_TEST);
+ }
+ }
+
+ // texture
+
+ void activeTexture(int? webglSlot) {
+ webglSlot ??= WebGL.TEXTURE0 + maxTextures - 1;
+
+ if (currentTextureSlot != webglSlot) {
+ gl.activeTexture(webglSlot);
+
+ currentTextureSlot = webglSlot;
+ }
+ }
+
+ void bindTexture(int webglType, WebGLTexture? webglTexture, [int? webglSlot]) {
+ if ( webglSlot == null ) {
+ if ( currentTextureSlot == null ) {
+ webglSlot = WebGL.TEXTURE0 + maxTextures - 1;
+ }
+ else {
+ webglSlot = currentTextureSlot;
+ }
+ }
+
+ BoundTexture? boundTexture = currentBoundTextures[webglSlot];
+
+ if (boundTexture == null) {
+ boundTexture = BoundTexture();
+ currentBoundTextures[webglSlot!] = boundTexture;
+ }
+
+ if (boundTexture.type != webglType || boundTexture.texture != webglTexture) {
+ if ( currentTextureSlot != webglSlot ) {
+ gl.activeTexture( webglSlot! );
+ currentTextureSlot = webglSlot;
+ }
+
+ gl.bindTexture(
+ webglType,
+ webglTexture ?? emptyTextures[webglType]
+ );
+
+ boundTexture.type = webglType;
+ boundTexture.texture = webglTexture;
+ }
+ }
+
+ void unbindTexture([WebGLTexture? texture]) {
+ final boundTexture = currentBoundTextures[currentTextureSlot];
+
+ if (boundTexture != null && boundTexture.type != null) {
+ gl.bindTexture(boundTexture.type!, kIsWeb?null:texture);
+ boundTexture.type = null;
+ boundTexture.texture = null;
+ }
+ }
+ void compressedTexSubImage3D(
+ int target,
+ int level,
+ int xoffset,
+ int yoffset,
+ int zoffset,
+ int width,
+ int height,
+ int depth,
+ int format,
+ TypedData? data,
+ ) {
+ gl.compressedTexSubImage3D(target,level,xoffset,yoffset,zoffset,width,height,depth,format,data);
+ }
+ void compressedTexImage3D(
+ int target,
+ int level,
+ int internalformat,
+ int width,
+ int height,
+ int depth,
+ int border,
+ TypedData? data,
+ ) {
+ gl.compressedTexImage3D(target,level,internalformat,width,height,depth,border,data);
+ }
+
+ void compressedTexImage2D(int target, int level, int internalformat, int width, int height, int border, TypedData? pixels) {
+ gl.compressedTexImage2D(target, level, internalformat, width, height, border, pixels);
+ }
+
+ void texSubImage2D(int target, int level, int x, int y, num width, num height, int glFormat, int glType, TypedData data) {
+ gl.texSubImage2D(target, level, x, y, width.toInt(), height.toInt(), glFormat, glType, data);
+ }
+
+ void texSubImage2DIf(int target, int level, int x, int y, int glFormat, int glType, ImageElement image) {
+ if (kIsWeb && image.data is! TypedData) {
+ texSubImage2DNoSize(WebGL.TEXTURE_2D, 0, 0, 0, glFormat, glType, image.data);
+ }
+ else {
+ texSubImage2D(WebGL.TEXTURE_2D, 0, 0, 0, image.width, image.height, glFormat, glType, image.data);
+ }
+ }
+
+ void texSubImage2DNoSize(int target, int level, int x, int y, int glFormat, int glType, data) {
+ if (kIsWeb) {
+ gl.texSubImage2D_NOSIZE(target, level, x, y, glFormat, glType, data);
+ }
+ else {
+ gl.texSubImage2D(target, level, 0, 0, x, y, glFormat, glType, data);
+ }
+ }
+
+ void texSubImage3D(int target, int level, int xoffset, int yoffset, int zoffset, int width, int height, int depth, int format, int type, TypedData? pixels) {
+ gl.texSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels);
+ }
+
+ void compressedTexSubImage2D(
+ int target,
+ int level,
+ int xoffset,
+ int yoffset,
+ int width,
+ int height,
+ int format,
+ TypedData? pixels,
+ ) {
+ gl.compressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, pixels);
+ }
+
+ void texStorage2D(int type, int levels, int glInternalFormat, int width, int height) {
+ gl.texStorage2D(type, levels, glInternalFormat, width, height);
+ }
+
+ void texStorage3D(target, levels, internalformat, width, height, depth) {
+ gl.texStorage3D(target, levels, internalformat, width.toInt(), height.toInt(), depth);
+ }
+
+ void texImage2DIf(int target, int level, int internalformat, int format, int type, image) {
+ if (kIsWeb) {
+ texImage2DNoSize(target, level, internalformat, format, type, image.data);
+ }
+ else {
+ texImage2D(target, level, internalformat, image.width, image.height, 0, format, type, image.data);
+ }
+ }
+
+ void texImage2D(int target, int level, int internalformat, int width, int height, border, int format, int type, data) {
+ gl.texImage2D(target, level, internalformat, width, height, border, format, type, data);
+ }
+
+ void texImage2DNoSize(int target, int level, int internalformat, int format, int type, data) {
+ gl.texImage2D_NOSIZE(target, level, internalformat, format, type, data);
+ }
+
+ void texImage3D(int target, int level, int internalformat, int width, int height, int depth, int border, int format,int type, offset) {
+ gl.texImage3D(target, level, internalformat, width, height, depth, border, format, type, offset);
+ }
+
+ @override
+ void scissor(Vector4 scissor) {
+ if (!currentScissor.equals(scissor)) {
+ gl.scissor(scissor.x.toInt(), scissor.y.toInt(), scissor.z.toInt(), scissor.w.toInt());
+ currentScissor.setFrom(scissor);
+ }
+ }
+
+ @override
+ void viewport(Vector4 viewport) {
+ if (!currentViewport.equals(viewport)) {
+ gl.viewport(viewport.x.toInt(), viewport.y.toInt(), viewport.z.toInt(), viewport.w.toInt());
+ currentViewport.setFrom(viewport);
+ }
+ }
+ void updateUBOMapping(UniformsGroup uniformsGroup, Program program ) {
+ dynamic mapping = uboProgramMap.get( program );
+
+ if ( mapping == null ) {
+ mapping = WeakMap();
+ uboProgramMap.set( program, mapping );
+ }
+
+ dynamic blockIndex = mapping.get( uniformsGroup );
+
+ if ( blockIndex == null ) {
+ blockIndex = gl.getUniformBlockIndex( program, uniformsGroup.name );
+ mapping.set( uniformsGroup, blockIndex );
+ }
+ }
+
+ void uniformBlockBinding( uniformsGroup, program ) {
+ final mapping = uboProgramMap.get( program );
+ final blockIndex = mapping.get( uniformsGroup );
+
+ if ( uboBindings.get( program ) != blockIndex ) {
+ // bind shader specific block index to global block point
+ gl.uniformBlockBinding( program, blockIndex, uniformsGroup['__bindingPointIndex'] );
+ uboBindings.set( program, blockIndex );
+ }
+ }
+
+ @override
+ void reset() {
+ gl.disable(WebGL.BLEND);
+ gl.disable(WebGL.CULL_FACE);
+ gl.disable(WebGL.DEPTH_TEST);
+ gl.disable(WebGL.POLYGON_OFFSET_FILL);
+ gl.disable(WebGL.SCISSOR_TEST);
+ gl.disable(WebGL.STENCIL_TEST);
+ gl.disable(WebGL.SAMPLE_ALPHA_TO_COVERAGE);
+
+ gl.blendEquation(WebGL.FUNC_ADD);
+ gl.blendFunc(WebGL.ONE, WebGL.ZERO);
+ gl.blendFuncSeparate(WebGL.ONE, WebGL.ZERO, WebGL.ONE, WebGL.ZERO);
+
+ gl.colorMask(true, true, true, true);
+ gl.clearColor(0, 0, 0, 0);
+
+ gl.depthMask(true);
+ gl.depthFunc(WebGL.LESS);
+ gl.clearDepth(1);
+
+ gl.stencilMask(0xffffffff);
+ gl.stencilFunc(WebGL.ALWAYS, 0, 0xffffffff);
+ gl.stencilOp(WebGL.KEEP, WebGL.KEEP, WebGL.KEEP);
+ gl.clearStencil(0);
+
+ gl.cullFace(WebGL.BACK);
+ gl.frontFace(WebGL.CCW);
+ gl.polygonOffset(0, 0);
+ gl.activeTexture(WebGL.TEXTURE0);
+
+ gl.bindFramebuffer(WebGL.DRAW_FRAMEBUFFER, null); // Equivalent to gl.FRAMEBUFFER
+ gl.bindFramebuffer(WebGL.READ_FRAMEBUFFER, null);
+ gl.bindFramebuffer(WebGL.FRAMEBUFFER, null);
+
+ gl.useProgram(null);
+ gl.lineWidth(1);
+ gl.scissor(0, 0, 0, 0);
+ gl.viewport(0, 0, 0, 0);
+
+ // reset internals
+
+ enabledCapabilities = {};
+
+ currentTextureSlot = null;
+ currentBoundTextures = {};
+
+ xrFramebuffer = null;
+ currentBoundFramebuffers = {};
+ currentDrawbuffers = WeakMap();
+ defaultDrawbuffers = [];
+
+ currentProgram = null;
+
+ currentBlendingEnabled = false;
+ currentBlending = null;
+ currentBlendEquation = null;
+ currentBlendSrc = null;
+ currentBlendDst = null;
+ currentBlendEquationAlpha = null;
+ currentBlendSrcAlpha = null;
+ currentBlendDstAlpha = null;
+ currentPremultipledAlpha = false;
+
+ currentFlipSided = null;
+ currentCullFace = null;
+
+ currentLineWidth = null;
+
+ currentPolygonOffsetFactor = null;
+ currentPolygonOffsetUnits = null;
+
+ currentScissor.setValues( 0, 0, gl.width.toDouble(), gl.height.toDouble());
+ currentViewport.setValues( 0, 0, gl.width.toDouble(), gl.height.toDouble());
+
+ colorBuffer.reset();
+ depthBuffer.reset();
+ stencilBuffer.reset();
+ }
+
+ @override
+ void dispose(){
+ if(_didDispose) return;
+ _didDispose = true;
+
+ emptyTextures.clear();
+ equationToGL.clear();
+ factorToGL.clear();
+ buffers.clear();
+ enabledCapabilities.clear();
+ currentBoundFramebuffers.clear();
+ defaultDrawbuffers.clear();
+ currentBoundTextures.clear();
+
+ extensions.dispose();
+ currentDrawbuffers.dispose();
+ }
+}
+
+class ColorBuffer {
+ RenderingContext gl;
+
+ bool locked = false;
+
+ late Function enable;
+ late Function disable;
+
+ Vector4 color = Vector4.identity();
+ bool? currentColorMask;
+ Vector4 currentColorClear = Vector4(0, 0, 0, 0);
+
+ ColorBuffer(this.gl);
+
+ setMask(bool colorMask) {
+ if (currentColorMask != colorMask && !locked) {
+ gl.colorMask(colorMask, colorMask, colorMask, colorMask);
+ currentColorMask = colorMask;
+ }
+ }
+
+ setLocked(lock) {
+ locked = lock;
+ }
+
+ setClear(double r, double g, double b, double a, bool premultipliedAlpha) {
+ if (premultipliedAlpha == true) {
+ r *= a;
+ g *= a;
+ b *= a;
+ }
+
+ color.setValues(r, g, b, a);
+
+ if (currentColorClear.equals(color) == false) {
+ gl.clearColor(r, g, b, a);
+ currentColorClear.setFrom(color);
+ }
+ }
+
+ void reset() {
+ locked = false;
+
+ currentColorMask = null;
+ currentColorClear.setValues(-1, 0, 0, 0); // set to invalid state
+ }
+}
+
+class DepthBuffer {
+ RenderingContext gl;
+
+ bool locked = false;
+ bool reversed = false;
+
+ late Function enable;
+ late Function disable;
+
+ bool? currentDepthMask;
+
+ int? currentDepthFunc;
+ double? currentDepthClear;
+ AngleExtensions extensions;
+
+ DepthBuffer(this.gl,this.extensions);
+
+ void setReversed(bool value ){
+ if ( reversed != value ) {
+ final ext = extensions.get( 'EXT_clip_control' );
+ if ( reversed ) {
+ ext.clipControlEXT( ext.LOWER_LEFT_EXT, ext.ZERO_TO_ONE_EXT );
+ } else {
+ ext.clipControlEXT( ext.LOWER_LEFT_EXT, ext.NEGATIVE_ONE_TO_ONE_EXT );
+ }
+ final oldDepth = currentDepthClear;
+ currentDepthClear = null;
+ this.setClear( oldDepth! );
+ }
+
+ reversed = value;
+ }
+
+ bool getReversed() {
+ return reversed;
+ }
+
+ void setTest(depthTest) {
+ if (depthTest) {
+ enable(WebGL.DEPTH_TEST);
+ } else {
+ disable(WebGL.DEPTH_TEST);
+ }
+ }
+
+ void setMask(bool depthMask) {
+ if (currentDepthMask != depthMask && !locked) {
+ gl.depthMask(depthMask);
+ currentDepthMask = depthMask;
+ }
+ }
+
+ void setFunc(int? depthFunc) {
+ if (currentDepthFunc != depthFunc) {
+ if (depthFunc != null) {
+ switch (depthFunc) {
+ case NeverDepth:
+ gl.depthFunc(WebGL.NEVER);
+ break;
+
+ case AlwaysDepth:
+ gl.depthFunc(WebGL.ALWAYS);
+ break;
+
+ case LessDepth:
+ gl.depthFunc(WebGL.LESS);
+ break;
+
+ case LessEqualDepth:
+ gl.depthFunc(WebGL.LEQUAL);
+ break;
+
+ case EqualDepth:
+ gl.depthFunc(WebGL.EQUAL);
+ break;
+
+ case GreaterEqualDepth:
+ gl.depthFunc(WebGL.GEQUAL);
+ break;
+
+ case GreaterDepth:
+ gl.depthFunc(WebGL.GREATER);
+ break;
+
+ case NotEqualDepth:
+ gl.depthFunc(WebGL.NOTEQUAL);
+ break;
+
+ default:
+ gl.depthFunc(WebGL.LEQUAL);
+ }
+ } else {
+ gl.depthFunc(WebGL.LEQUAL);
+ }
+
+ currentDepthFunc = depthFunc;
+ }
+ }
+
+ void setLocked(lock) {
+ locked = lock;
+ }
+
+ void setClear(double depth) {
+ if (currentDepthClear != depth) {
+ gl.clearDepth(depth);
+ currentDepthClear = depth;
+ }
+ }
+
+ void reset() {
+ locked = false;
+
+ currentDepthMask = null;
+ currentDepthFunc = null;
+ currentDepthClear = null;
+ }
+}
+
+class StencilBuffer {
+ RenderingContext gl;
+
+ bool locked = false;
+
+ late Function enable;
+ late Function disable;
+
+ int? currentStencilMask;
+ int? currentStencilFunc;
+ int? currentStencilRef;
+ int? currentStencilFuncMask;
+ int? currentStencilFail;
+ int? currentStencilZFail;
+ int? currentStencilZPass;
+ int? currentStencilClear;
+
+ StencilBuffer(this.gl);
+
+ void setTest(bool stencilTest) {
+ if (!locked) {
+ if (stencilTest) {
+ enable(WebGL.STENCIL_TEST);
+ } else {
+ disable(WebGL.STENCIL_TEST);
+ }
+ }
+ }
+
+ void setMask(int stencilMask) {
+ if (currentStencilMask != stencilMask && !locked) {
+ gl.stencilMask(stencilMask);
+ currentStencilMask = stencilMask;
+ }
+ }
+
+ void setFunc(int stencilFunc, int stencilRef, int stencilMask) {
+ if (currentStencilFunc != stencilFunc || currentStencilRef != stencilRef || currentStencilFuncMask != stencilMask) {
+ gl.stencilFunc(stencilFunc, stencilRef, stencilMask);
+
+ currentStencilFunc = stencilFunc;
+ currentStencilRef = stencilRef;
+ currentStencilFuncMask = stencilMask;
+ }
+ }
+
+ void setOp(int stencilFail, int stencilZFail, int stencilZPass) {
+ if (currentStencilFail != stencilFail ||
+ currentStencilZFail != stencilZFail ||
+ currentStencilZPass != stencilZPass) {
+ gl.stencilOp(stencilFail, stencilZFail, stencilZPass);
+
+ currentStencilFail = stencilFail;
+ currentStencilZFail = stencilZFail;
+ currentStencilZPass = stencilZPass;
+ }
+ }
+
+ void setLocked(bool lock) {
+ locked = lock;
+ }
+
+ void setClear(int stencil) {
+ if (currentStencilClear != stencil) {
+ gl.clearStencil(stencil);
+ currentStencilClear = stencil;
+ }
+ }
+
+ void reset() {
+ locked = false;
+
+ currentStencilMask = null;
+ currentStencilFunc = null;
+ currentStencilRef = null;
+ currentStencilFuncMask = null;
+ currentStencilFail = null;
+ currentStencilZFail = null;
+ currentStencilZPass = null;
+ currentStencilClear = null;
+ }
+}
+
+class BoundTexture {
+ int? type;
+ dynamic texture;
+
+ BoundTexture([this.type, this.texture]);
+}
diff --git a/packages/three_js_angle_renderer/lib/angle/angle_textures.dart b/packages/three_js_angle_renderer/lib/angle/angle_textures.dart
new file mode 100755
index 00000000..88300d94
--- /dev/null
+++ b/packages/three_js_angle_renderer/lib/angle/angle_textures.dart
@@ -0,0 +1,1632 @@
+part of three_webgl;
+
+class AngleTextures {
+ bool _didDispose = false;
+ RenderingContext gl;
+ RenderingContext get _gl => gl;
+ AngleExtensions extensions;
+ AngleState state;
+ AngleProperties properties;
+ AngleCapabilities capabilities;
+ AngleUtils utils;
+ AngleInfo info;
+ bool isWebGL2 = true;
+
+ late int maxTextures;
+ late int maxCubemapSize;
+ late int maxTextureSize;
+ late int maxSamples;
+
+ bool supportsInvalidateFramenbuffer = false;
+
+ //final _imageDimensions = Vector2();
+ final WeakMap _videoTextures = WeakMap();
+
+ final WeakMap _sources = WeakMap();
+ // maps WebglTexture objects to instances of Source
+
+ Map wrappingToGL = {};
+ Map filterToGL = {};
+ Map compareToGL = {};
+
+ dynamic multisampledRenderToTextureExtension;
+ dynamic multisampledRTTExt;
+ final bool supportsInvalidateFramebuffer = false;//typeof navigator === 'undefined' ? false : /OculusBrowser/g.test( navigator.userAgent );
+
+ AngleTextures(this.gl, this.extensions, this.state, this.properties, this.capabilities, this.utils, this.info) {
+ maxTextures = capabilities.maxTextures;
+ maxCubemapSize = capabilities.maxCubemapSize;
+ maxTextureSize = capabilities.maxTextureSize;
+ maxSamples = capabilities.maxSamples;
+
+ multisampledRTTExt = extensions.has( 'WEBGL_multisampled_render_to_texture' )? extensions.get( 'WEBGL_multisampled_render_to_texture' ) : null;
+ multisampledRenderToTextureExtension = extensions.has('WEBGL_multisampled_render_to_texture')? extensions.get('WEBGL_multisampled_render_to_texture'): null;
+
+ wrappingToGL[RepeatWrapping] = WebGL.REPEAT;
+ wrappingToGL[ClampToEdgeWrapping] = WebGL.CLAMP_TO_EDGE;
+ wrappingToGL[MirroredRepeatWrapping] = WebGL.MIRRORED_REPEAT;
+
+ filterToGL[NearestFilter] = WebGL.NEAREST;
+ filterToGL[NearestMipmapNearestFilter] = WebGL.NEAREST_MIPMAP_NEAREST;
+ filterToGL[NearestMipmapLinearFilter] = WebGL.NEAREST_MIPMAP_LINEAR;
+ filterToGL[LinearFilter] = WebGL.LINEAR;
+ filterToGL[LinearMipmapNearestFilter] = WebGL.LINEAR_MIPMAP_NEAREST;
+ filterToGL[LinearMipmapLinearFilter] = WebGL.LINEAR_MIPMAP_LINEAR;
+
+ compareToGL[ NeverCompare ] = WebGL.NEVER;
+ compareToGL[ AlwaysCompare ] = WebGL.ALWAYS;
+ compareToGL[ LessCompare ] = WebGL.LESS;
+ compareToGL[ LessEqualCompare ] = WebGL.LEQUAL;
+ compareToGL[ EqualCompare ] = WebGL.EQUAL;
+ compareToGL[ GreaterEqualCompare ] = WebGL.GEQUAL;
+ compareToGL[ GreaterCompare ] = WebGL.GREATER;
+ compareToGL[ NotEqualCompare ] = WebGL.NOTEQUAL;
+
+ // TODO FIXME when on web && is OculusBrowser
+ // supportsInvalidateFramenbuffer = kIsWeb && RegExp(r"OculusBrowser").hasMatch( navigator.userAgent );
+ }
+
+ bool isPowerOfTwo(image) {
+ return MathUtils.isPowerOfTwo(image.width.toInt()) && MathUtils.isPowerOfTwo(image.height.toInt());
+ }
+
+ bool textureNeedsGenerateMipmaps(Texture texture) {
+ return texture.generateMipmaps;
+ }
+
+ generateMipmap(target) {
+ gl.generateMipmap(target);
+ }
+
+ int getInternalFormat(internalFormatName, int glFormat, int glType, String colorSpace, [bool forceLinearTransfer = false]) {
+ if ( internalFormatName != null ) {
+ if ( WebGL.get( internalFormatName ) != null ) return WebGL.get( internalFormatName )!;
+ console.warning( 'THREE.WebGLRenderer: Attempt to use non-existing WebGL internal format \'' + internalFormatName + '\'' );
+ }
+
+ int internalFormat = glFormat;
+
+ if ( glFormat == WebGL.RED ) {
+ if ( glType == WebGL.FLOAT ) internalFormat = WebGL.R32F;
+ if ( glType == WebGL.HALF_FLOAT ) internalFormat = WebGL.R16F;
+ if ( glType == WebGL.UNSIGNED_BYTE ) internalFormat = WebGL.R8;
+ }
+
+ if ( glFormat == WebGL.RED_INTEGER ) {
+ if ( glType == WebGL.UNSIGNED_BYTE ) internalFormat = WebGL.R8UI;
+ if ( glType == WebGL.UNSIGNED_SHORT ) internalFormat = WebGL.R16UI;
+ if ( glType == WebGL.UNSIGNED_INT ) internalFormat = WebGL.R32UI;
+ if ( glType == WebGL.BYTE ) internalFormat = WebGL.R8I;
+ if ( glType == WebGL.SHORT ) internalFormat = WebGL.R16I;
+ if ( glType == WebGL.INT ) internalFormat = WebGL.R32I;
+ }
+
+ if ( glFormat == WebGL.RG ) {
+ if ( glType == WebGL.FLOAT ) internalFormat = WebGL.RG32F;
+ if ( glType == WebGL.HALF_FLOAT ) internalFormat = WebGL.RG16F;
+ if ( glType == WebGL.UNSIGNED_BYTE ) internalFormat = WebGL.RG8;
+ }
+
+ if ( glFormat == WebGL.RG_INTEGER ) {
+ if ( glType == WebGL.UNSIGNED_BYTE ) internalFormat = WebGL.RG8UI;
+ if ( glType == WebGL.UNSIGNED_SHORT ) internalFormat = WebGL.RG16UI;
+ if ( glType == WebGL.UNSIGNED_INT ) internalFormat = WebGL.RG32UI;
+ if ( glType == WebGL.BYTE ) internalFormat = WebGL.RG8I;
+ if ( glType == WebGL.SHORT ) internalFormat = WebGL.RG16I;
+ if ( glType == WebGL.INT ) internalFormat = WebGL.RG32I;
+ }
+
+ if ( glFormat == WebGL.RGB_INTEGER ) {
+ if ( glType == WebGL.UNSIGNED_BYTE ) internalFormat = WebGL.RGB8UI;
+ if ( glType == WebGL.UNSIGNED_SHORT ) internalFormat = WebGL.RGB16UI;
+ if ( glType == WebGL.UNSIGNED_INT ) internalFormat = WebGL.RGB32UI;
+ if ( glType == WebGL.BYTE ) internalFormat = WebGL.RGB8I;
+ if ( glType == WebGL.SHORT ) internalFormat = WebGL.RGB16I;
+ if ( glType == WebGL.INT ) internalFormat = WebGL.RGB32I;
+ }
+
+ if ( glFormat == WebGL.RGBA_INTEGER ) {
+
+ if ( glType == WebGL.UNSIGNED_BYTE ) internalFormat = WebGL.RGBA8UI;
+ if ( glType == WebGL.UNSIGNED_SHORT ) internalFormat = WebGL.RGBA16UI;
+ if ( glType == WebGL.UNSIGNED_INT ) internalFormat = WebGL.RGBA32UI;
+ if ( glType == WebGL.SHORT ) internalFormat = WebGL.RGBA16I;
+ if ( glType == WebGL.INT ) internalFormat = WebGL.RGBA32I;
+
+ }
+
+ if ( glFormat == WebGL.RGB ) {
+ if ( glType == WebGL.UNSIGNED_INT_5_9_9_9_REV ) internalFormat = WebGL.RGB9_E5;
+ }
+
+ if ( glFormat == WebGL.RGBA ) {
+ final String transfer = forceLinearTransfer ? LinearTransfer : ColorManagement.getTransfer( ColorSpace.fromString(colorSpace));
+
+ if ( glType == WebGL.FLOAT ) internalFormat = WebGL.RGBA32F;
+ if ( glType == WebGL.HALF_FLOAT ) internalFormat = WebGL.RGBA16F;
+ if ( glType == WebGL.UNSIGNED_BYTE ) internalFormat = ( transfer == SRGBTransfer ) ? WebGL.SRGB8_ALPHA8 : WebGL.RGBA8;
+ if ( glType == WebGL.UNSIGNED_SHORT_4_4_4_4 ) internalFormat = WebGL.RGBA4;
+ if ( glType == WebGL.UNSIGNED_SHORT_5_5_5_1 ) internalFormat = WebGL.RGB5_A1;
+ }
+
+ if ( internalFormat == WebGL.R16F || internalFormat == WebGL.R32F ||
+ internalFormat == WebGL.RG16F || internalFormat == WebGL.RG32F ||
+ internalFormat == WebGL.RGBA16F || internalFormat == WebGL.RGBA32F ) {
+ extensions.get( 'EXT_color_buffer_float' );
+ }
+
+ return internalFormat;
+ }
+
+ int getInternalDepthFormat(bool useStencil, [int? depthType ]) {
+ late int glInternalFormat;
+
+ if ( useStencil ) {
+ if ( depthType == null || depthType == UnsignedIntType || depthType == UnsignedInt248Type ) {
+ glInternalFormat = WebGL.DEPTH24_STENCIL8;
+ } else if ( depthType == FloatType ) {
+ glInternalFormat = WebGL.DEPTH32F_STENCIL8;
+ } else if ( depthType == UnsignedShortType ) {
+ glInternalFormat = WebGL.DEPTH24_STENCIL8;
+ console.warning( 'DepthTexture: 16 bit depth attachment is not supported with stencil. Using 24-bit attachment.' );
+ }
+ } else {
+ if ( depthType == null || depthType == UnsignedIntType || depthType == UnsignedInt248Type ) {
+ glInternalFormat = WebGL.DEPTH_COMPONENT24;
+ } else if ( depthType == FloatType ) {
+ glInternalFormat = WebGL.DEPTH_COMPONENT32F;
+ } else if ( depthType == UnsignedShortType ) {
+ glInternalFormat = WebGL.DEPTH_COMPONENT16;
+ }
+ }
+
+ return glInternalFormat;
+ }
+
+ int getMipLevels(Texture texture, image) {
+ if (
+ textureNeedsGenerateMipmaps(texture)||
+ (texture is FramebufferTexture &&
+ texture.minFilter != NearestFilter &&
+ texture.minFilter != LinearFilter
+ )
+ ){
+ return MathUtils.log2(
+ math.max(
+ image.width,
+ image.height
+ )
+ ).toInt() + 1;
+ }
+ else if (texture.mipmaps.isNotEmpty) {
+ // user-defined mipmaps
+ return texture.mipmaps.length;
+ }
+ else if (texture is CompressedTexture && texture.image is List) {
+ // Dart: TODO texture.image is List ???
+ return image.mipmaps.length;
+ }
+ else {
+ // texture without mipmaps (only base level)
+ return 1;
+ }
+ }
+
+ //
+
+ void onTextureDispose(Event event) {
+ final texture = event.target;
+
+ texture.removeEventListener('dispose', onTextureDispose);
+
+ deallocateTexture(texture);
+
+ if (texture is VideoTexture) {
+ _videoTextures.delete(texture);
+ }
+ }
+
+ void onRenderTargetDispose(Event event) {
+ final renderTarget = event.target;
+ renderTarget.removeEventListener('dispose', onRenderTargetDispose);
+ deallocateRenderTarget(renderTarget);
+ }
+
+ void deallocateTexture(Texture texture) {
+ final textureProperties = properties.get(texture);
+
+ if (textureProperties["__webglInit"] == null) return;
+
+ final source = texture.source;
+ final webglTextures = _sources.get(source);
+
+ if (webglTextures != null) {
+ Map webglTexture = webglTextures[textureProperties["__cacheKey"]];
+ webglTexture["usedTimes"]--;
+
+ if (webglTexture["usedTimes"] == 0) {
+ deleteTexture(texture);
+ }
+
+ if (webglTextures.keys.length == 0) {
+ _sources.delete(source);
+ }
+ }
+
+ properties.remove(texture);
+ }
+
+ void deleteTexture(Texture texture) {
+ final textureProperties = properties.get(texture);
+ _gl.deleteTexture(textureProperties["__webglTexture"]);
+
+ final source = texture.source;
+ Map webglTextures = _sources.get(source);
+ webglTextures.remove(textureProperties["__cacheKey"]);
+
+ info.memory["textures"] = info.memory["textures"]! - 1;
+ }
+
+ void deallocateRenderTarget(RenderTarget renderTarget) {
+ final renderTargetProperties = properties.get(renderTarget);
+
+ if (renderTarget.depthTexture != null) {
+ renderTarget.depthTexture!.dispose();
+ properties.remove( renderTarget.depthTexture );
+ }
+
+ if (renderTarget is CubeRenderTarget) {
+ for (int i = 0; i < 6; i++) {
+ gl.deleteFramebuffer(renderTargetProperties["__webglFramebuffer"][i]);
+ if (renderTargetProperties['__webglFramebuffer'][ i ] is List) {
+ for (int level = 0; level < renderTargetProperties['__webglFramebuffer'][ i ].length; level ++ ) _gl.deleteFramebuffer( renderTargetProperties['__webglFramebuffer'][ i ][ level ] );
+ } else {
+ _gl.deleteFramebuffer( renderTargetProperties['__webglFramebuffer'][ i ] );
+ }
+ if (renderTargetProperties["__webglDepthbuffer"] != null) {
+ gl.deleteRenderbuffer(renderTargetProperties["__webglDepthbuffer"][i]);
+ }
+ }
+ } else {
+ if (renderTargetProperties['__webglFramebuffer'] is List) {
+ for (int level = 0; level < renderTargetProperties['__webglFramebuffer'].length; level ++ ) _gl.deleteFramebuffer( renderTargetProperties['__webglFramebuffer'][ level ] );
+ } else {
+ _gl.deleteFramebuffer( renderTargetProperties['__webglFramebuffer'] );
+ }
+
+ gl.deleteFramebuffer(renderTargetProperties["__webglFramebuffer"]);
+ if (renderTargetProperties["__webglDepthbuffer"] != null) {
+ gl.deleteRenderbuffer(renderTargetProperties["__webglDepthbuffer"]);
+ }
+ if (renderTargetProperties["__webglMultisampledFramebuffer"] != null) {
+ gl.deleteFramebuffer(renderTargetProperties["__webglMultisampledFramebuffer"]);
+ }
+ if (renderTargetProperties["__webglColorRenderbuffer"] != null) {
+ for (int i = 0; i < renderTargetProperties['__webglColorRenderbuffer'].length; i ++ ) {
+ if ( renderTargetProperties['__webglColorRenderbuffer'][ i ] != null) _gl.deleteRenderbuffer( renderTargetProperties['__webglColorRenderbuffer'][ i ] );
+ }
+ }
+ if (renderTargetProperties["__webglDepthRenderbuffer"] != null) {
+ gl.deleteRenderbuffer(renderTargetProperties["__webglDepthRenderbuffer"]);
+ }
+ }
+
+ final textures = renderTarget.textures;
+ for (int i = 0, il = textures.length; i < il; i++) {
+ final attachmentProperties = properties.get(textures[i]);
+
+ if (attachmentProperties["__webglTexture"] != null) {
+ gl.deleteTexture(attachmentProperties["__webglTexture"]);
+
+ info.memory["textures"] = info.memory["textures"]! - 1;
+ }
+
+ properties.remove(textures[i]);
+ }
+
+ properties.remove(renderTarget);
+ }
+
+ //
+
+ int textureUnits = 0;
+
+ void resetTextureUnits() => textureUnits = 0;
+
+ int allocateTextureUnit() {
+ int textureUnit = textureUnits;
+
+ if (textureUnit >= maxTextures) {
+ console.warning('WebGLTextures: Trying to use $textureUnit texture units while this GPU supports only $maxTextures');
+ }
+
+ textureUnits += 1;
+
+ return textureUnit;
+ }
+
+ String getTextureCacheKey(Texture texture) {
+ final array = [];
+
+ array.add(texture.wrapS);
+ array.add(texture.wrapT);
+ array.add(texture.wrapR);
+ array.add(texture.magFilter);
+ array.add(texture.minFilter);
+ array.add(texture.anisotropy);
+ array.add(texture.internalFormat);
+ array.add(texture.format);
+ array.add(texture.type);
+ array.add(texture.generateMipmaps);
+ array.add(texture.premultiplyAlpha);
+ array.add(texture.flipY);
+ array.add(texture.unpackAlignment);
+ array.add(texture.colorSpace);
+
+ return array.join();
+ }
+
+ void setTexture2D(Texture texture, int slot) {
+ final textureProperties = properties.get(texture);
+
+ if (texture is VideoTexture) updateVideoTexture(texture);
+
+ if (!texture.isRenderTargetTexture && texture.version > 0 && textureProperties["__version"] != texture.version) {
+ final image = texture.image;
+ if (image == null) {
+ console.warning('WebGLRenderer: Texture marked for update but image is null');
+ }
+ else if (image.complete == false) {
+ console.warning('WebGLRenderer: Texture marked for update but image is incomplete');
+ }
+ else {
+ uploadTexture(textureProperties, texture, slot);
+ return;
+ }
+ }
+ state.activeTexture(WebGL.TEXTURE0 + slot);
+ state.bindTexture(WebGL.TEXTURE_2D, textureProperties["__webglTexture"]);
+ }
+
+ void setTexture2DArray(Texture texture, int slot) {
+ final textureProperties = properties.get(texture);
+
+ if (texture.version > 0 && textureProperties["__version"] != texture.version) {
+ uploadTexture(textureProperties, texture, slot);
+ return;
+ }
+ state.activeTexture(WebGL.TEXTURE0 + slot);
+ state.bindTexture(WebGL.TEXTURE_2D_ARRAY, textureProperties["__webglTexture"]);
+ }
+
+ void setTexture3D(Texture texture, int slot) {
+ final textureProperties = properties.get(texture);
+
+ if (texture.version > 0 && textureProperties["__version"] != texture.version) {
+ uploadTexture(textureProperties, texture, slot);
+ return;
+ }
+ state.activeTexture(WebGL.TEXTURE0 + slot);
+ state.bindTexture(WebGL.TEXTURE_3D, textureProperties["__webglTexture"]);
+ }
+
+ void setTextureCube(Texture texture, int slot) {
+ final textureProperties = properties.get(texture);
+
+ if (texture.version > 0 && textureProperties["__version"] != texture.version) {
+ uploadCubeTexture(textureProperties, texture, slot);
+ return;
+ }
+
+ state.activeTexture(WebGL.TEXTURE0 + slot);
+ state.bindTexture(WebGL.TEXTURE_CUBE_MAP, textureProperties["__webglTexture"]);
+ }
+
+ void setTextureParameters(textureType, Texture texture, [supportsMips]) {
+ if ( texture.type == FloatType && !extensions.has( 'OES_texture_float_linear' ) &&
+ ( texture.magFilter == LinearFilter || texture.magFilter == LinearMipmapNearestFilter || texture.magFilter == NearestMipmapLinearFilter || texture.magFilter == LinearMipmapLinearFilter ||
+ texture.minFilter == LinearFilter || texture.minFilter == LinearMipmapNearestFilter || texture.minFilter == NearestMipmapLinearFilter || texture.minFilter == LinearMipmapLinearFilter ) ) {
+ console.warning( 'THREE.WebGLRenderer: Unable to use linear filtering with floating point textures. OES_texture_float_linear not supported on this device.' );
+ }
+
+ _gl.texParameteri( textureType, WebGL.TEXTURE_WRAP_S, wrappingToGL[ texture.wrapS ]! );
+ _gl.texParameteri( textureType, WebGL.TEXTURE_WRAP_T, wrappingToGL[ texture.wrapT ]! );
+
+ if ( textureType == WebGL.TEXTURE_3D || textureType == WebGL.TEXTURE_2D_ARRAY ) {
+ _gl.texParameteri( textureType, WebGL.TEXTURE_WRAP_R, wrappingToGL[ texture.wrapR ]! );
+ }
+
+ _gl.texParameteri( textureType, WebGL.TEXTURE_MAG_FILTER, filterToGL[ texture.magFilter ]! );
+ _gl.texParameteri( textureType, WebGL.TEXTURE_MIN_FILTER, filterToGL[ texture.minFilter ]! );
+
+ if (texture is DepthTexture && texture.compareFunction != null) {
+ _gl.texParameteri( textureType, WebGL.TEXTURE_COMPARE_MODE, WebGL.COMPARE_REF_TO_TEXTURE );
+ _gl.texParameteri( textureType, WebGL.TEXTURE_COMPARE_FUNC, compareToGL[ texture.compareFunction ]! );
+ }
+
+ if ( extensions.has( 'EXT_texture_filter_anisotropic' )) {
+ if ( texture.magFilter == NearestFilter ) return;
+ if ( texture.minFilter != NearestMipmapLinearFilter && texture.minFilter != LinearMipmapLinearFilter ) return;
+ if ( texture.type == FloatType && !extensions.has( 'OES_texture_float_linear' )) return; // verify extension
+
+ if ( texture.anisotropy > 1 || properties.get( texture )['__currentAnisotropy'] != null) {
+ // final extension = extensions.get( 'EXT_texture_filter_anisotropic' );
+ // if (kIsWeb && !kIsWasm) {
+ // gl.texParameterf(textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT,math.min(texture.anisotropy, capabilities.getMaxAnisotropy()).toDouble());
+ // }
+ // else {
+ gl.texParameterf(textureType, WebGL.TEXTURE_MAX_ANISOTROPY_EXT,math.min(texture.anisotropy, capabilities.getMaxAnisotropy()).toDouble());
+ //}
+ properties.get( texture )['__currentAnisotropy'] = texture.anisotropy;
+ }
+ }
+ }
+
+ bool initTexture(Map textureProperties, Texture texture) {
+ bool forceUpload = false;
+
+ if (textureProperties["__webglInit"] != true) {
+ textureProperties["__webglInit"] = true;
+
+ texture.addEventListener('dispose', onTextureDispose);
+ }
+
+ final source = texture.source;
+ Map? webglTextures = _sources.get(source);
+
+ if (webglTextures == null) {
+ webglTextures = {};
+ _sources.set(source, webglTextures);
+ }
+
+ // check if there is already a WebGLTexture object for the given texture parameters
+
+ final textureCacheKey = getTextureCacheKey(texture);
+
+ if (textureCacheKey != textureProperties["__cacheKey"]) {
+ // if not, create a new instance of WebGLTexture
+
+ if (webglTextures[textureCacheKey] == null) {
+ // create new entry
+
+ webglTextures[textureCacheKey] = {"texture": _gl.createTexture(), "usedTimes": 0};
+
+ info.memory["textures"] = info.memory["textures"]! + 1;
+
+ // when a new instance of WebGLTexture was created, a texture upload is required
+ // even if the image contents are identical
+
+ forceUpload = true;
+ }
+
+ webglTextures[textureCacheKey]["usedTimes"]++;
+
+ // every time the texture cache key changes, it's necessary to check if an instance of
+ // WebGLTexture can be deleted in order to avoid a memory leak.
+
+ final webglTexture = webglTextures[textureProperties["__cacheKey"]];
+
+ if (webglTexture != null) {
+ webglTextures[textureProperties["__cacheKey"]]["usedTimes"]--;
+
+ if (webglTexture["usedTimes"] == 0) {
+ deleteTexture(texture);
+ }
+ }
+
+ // store references to cache key and WebGLTexture object
+
+ textureProperties["__cacheKey"] = textureCacheKey;
+ textureProperties["__webglTexture"] = webglTextures[textureCacheKey]["texture"];
+ }
+
+ return forceUpload;
+ }
+
+ int getRow(int index, num rowLength, int componentStride ) {
+ return ( ( index / componentStride ).floor() / rowLength ).floor();
+ }
+
+ void updateTexture(Texture texture, ImageElement image, int glFormat, int glType ) {
+ int componentStride = 4; // only RGBA supported
+ final updateRanges = texture.updateRanges;
+
+ if ( updateRanges.length == 0 ) {
+ state.texSubImage2D( WebGL.TEXTURE_2D, 0, 0, 0, image.width, image.height, glFormat, glType, image.data );
+ }
+ else {
+
+ // Before applying update ranges, we merge any adjacent / overlapping
+ // ranges to reduce load on `gl.texSubImage2D`. Empirically, this has led
+ // to performance improvements for applications which make heavy use of
+ // update ranges. Likely due to GPU command overhead.
+ //
+ // Note that to reduce garbage collection between frames, we merge the
+ // update ranges in-place. This is safe because this method will clear the
+ // update ranges once updated.
+
+ updateRanges.sort( ( a, b ) => a.start - b.start );
+
+ // To merge the update ranges in-place, we work from left to right in the
+ // existing updateRanges array, merging ranges. This may result in a final
+ // array which is smaller than the original. This index tracks the last
+ // index representing a merged range, any data after this index can be
+ // trimmed once the merge algorithm is completed.
+ int mergeIndex = 0;
+
+ for (int i = 1; i < updateRanges.length; i ++ ) {
+ final previousRange = updateRanges[ mergeIndex ];
+ final range = updateRanges[ i ];
+
+ // Only merge if in the same row and overlapping/adjacent
+ final previousEnd = previousRange.start + previousRange.count;
+ final currentRow = getRow( range.start, image.width, componentStride );
+ final previousRow = getRow( previousRange.start, image.width, componentStride );
+
+ // We add one here to merge adjacent ranges. This is safe because ranges
+ // operate over positive integers.
+ if (
+ range.start <= previousEnd + 1 &&
+ currentRow == previousRow &&
+ getRow( range.start + range.count - 1, image.width, componentStride ) == currentRow // ensure range doesn't spill
+ ) {
+
+ previousRange.count = math.max(
+ previousRange.count,
+ range.start + range.count - previousRange.start
+ );
+
+ } else {
+ ++ mergeIndex;
+ updateRanges[ mergeIndex ] = range;
+ }
+ }
+
+ // Trim the array to only contain the merged ranges.
+ updateRanges.length = mergeIndex + 1;
+
+ final currentUnpackRowLen = _gl.getParameter( WebGL.UNPACK_ROW_LENGTH );
+ final currentUnpackSkipPixels = _gl.getParameter( WebGL.UNPACK_SKIP_PIXELS );
+ final currentUnpackSkipRows = _gl.getParameter( WebGL.UNPACK_SKIP_ROWS );
+
+ _gl.pixelStorei( WebGL.UNPACK_ROW_LENGTH, image.width.toInt() );
+
+ for (int i = 0, l = updateRanges.length; i < l; i ++ ) {
+ final range = updateRanges[ i ];
+
+ final pixelStart = ( range.start / componentStride ).floor();
+ final pixelCount = ( range.count / componentStride ).ceil();
+
+ final x = (pixelStart % image.width).toInt();
+ final y = ( pixelStart / image.width ).floor();
+
+ // Assumes update ranges refer to contiguous memory
+ final width = pixelCount;
+ final height = 1;
+
+ _gl.pixelStorei( WebGL.UNPACK_SKIP_PIXELS, x );
+ _gl.pixelStorei( WebGL.UNPACK_SKIP_ROWS, y );
+
+ state.texSubImage2D( WebGL.TEXTURE_2D, 0, x, y, width, height, glFormat, glType, image.data );
+ }
+
+ texture.clearUpdateRanges();
+
+ _gl.pixelStorei( WebGL.UNPACK_ROW_LENGTH, currentUnpackRowLen );
+ _gl.pixelStorei( WebGL.UNPACK_SKIP_PIXELS, currentUnpackSkipPixels );
+ _gl.pixelStorei( WebGL.UNPACK_SKIP_ROWS, currentUnpackSkipRows );
+ }
+ }
+
+ void uploadTexture(Map textureProperties, Texture texture, int slot) {
+ dynamic textureType = WebGL.TEXTURE_2D;
+
+ if (texture is DataArrayTexture) textureType = WebGL.TEXTURE_2D_ARRAY;
+ if (texture is Data3DTexture) textureType = WebGL.TEXTURE_3D;
+
+ final forceUpload = initTexture(textureProperties, texture);
+ final source = texture.source;
+
+ state.activeTexture(WebGL.TEXTURE0 + slot);
+ state.bindTexture(textureType, textureProperties["__webglTexture"]);
+
+ if (source.version != source.currentVersion || forceUpload) {
+
+ _gl.pixelStorei(WebGL.UNPACK_ALIGNMENT, texture.unpackAlignment);
+ if (kIsWeb) {
+ _gl.pixelStorei(WebGL.UNPACK_FLIP_Y_WEBGL, texture.flipY ? 1 : 0);
+ _gl.pixelStorei(WebGL.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha ? 1 : 0);
+ _gl.pixelStorei(WebGL.UNPACK_COLORSPACE_CONVERSION_WEBGL, WebGL.NONE);
+ }
+
+ dynamic image = texture.image;//resizeImage(texture.image, needsPowerOfTwo, false, maxTextureSize);
+ image = verifyColorSpace(texture, image);
+
+ final int glFormat = utils.convert(texture.format, texture.colorSpace);
+ int glType = utils.convert(texture.type);
+ int glInternalFormat = getInternalFormat(texture.internalFormat, glFormat, glType, texture.colorSpace, texture is VideoTexture);
+
+ setTextureParameters(textureType, texture);
+
+ dynamic mipmap;
+ final mipmaps = texture.mipmaps;
+
+ final useTexStorage = (isWebGL2 && texture is! VideoTexture);
+ final allocateMemory = (textureProperties["__version"] == null) || (forceUpload == true);
+ final dataReady = source.dataReady;
+ final levels = getMipLevels(texture, image);
+
+ if (texture is DepthTexture) {
+ glInternalFormat = getInternalDepthFormat( texture.format == DepthStencilFormat, texture.type );
+ if ( allocateMemory ) {
+ if ( useTexStorage ) {
+ state.texStorage2D( WebGL.TEXTURE_2D, 1, glInternalFormat, image.width, image.height );
+ } else {
+ state.texImage2D( WebGL.TEXTURE_2D, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, null );
+ }
+ }
+ }
+ else if (texture is DataTexture) {
+ // use manually created mipmaps if available
+ // if there are no manual mipmaps
+ // set 0 level mipmap and then use GL to generate other mipmap levels
+
+ if (mipmaps.isNotEmpty) {
+ if (useTexStorage && allocateMemory) {
+ state.texStorage2D(WebGL.TEXTURE_2D, levels, glInternalFormat, mipmaps[0].width, mipmaps[0].height);
+ }
+
+ for (int i = 0, il = mipmaps.length; i < il; i++) {
+ mipmap = mipmaps[i];
+ if (useTexStorage && dataReady) {
+ state.texSubImage2D(WebGL.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data);
+ }
+ else {
+ state.texImage2D(WebGL.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data);
+ }
+ }
+
+ texture.generateMipmaps = false;
+ }
+ else {
+
+ if (useTexStorage) {
+ if (allocateMemory) {
+ state.texStorage2D(WebGL.TEXTURE_2D, levels, glInternalFormat, image.width.toInt(), image.height.toInt());
+ }
+ if ( dataReady ) {
+ updateTexture( texture, image, glFormat, glType );
+ }
+ }
+ else {
+ state.texImage2D(WebGL.TEXTURE_2D, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, image.data);
+ }
+ }
+ }
+ else if (texture is CompressedTexture) {
+ if ( texture is CompressedArrayTexture ) {
+ if ( useTexStorage && allocateMemory ) {
+ state.texStorage3D( WebGL.TEXTURE_2D_ARRAY, levels, glInternalFormat, mipmaps[ 0 ].width, mipmaps[ 0 ].height, image.depth );
+ }
+
+ for ( int i = 0, il = mipmaps.length; i < il; i ++ ) {
+ mipmap = mipmaps[ i ];
+ if ( texture.format != RGBAFormat ) {
+ if ( glFormat > 0 ) {
+ if ( useTexStorage && dataReady) {
+ if ( texture.layerUpdates.isNotEmpty ) {
+ final layerByteLength = TextureUtils.getByteLength( mipmap.width, mipmap.height, texture.format, texture.type );
+ for ( final layerIndex in texture.layerUpdates ) {
+ final layerData = mipmap.data.subarray(
+ layerIndex * layerByteLength / mipmap.data.BYTES_PER_ELEMENT,
+ ( layerIndex + 1 ) * layerByteLength / mipmap.data.BYTES_PER_ELEMENT
+ );
+ state.compressedTexSubImage3D( WebGL.TEXTURE_2D_ARRAY, i, 0, 0, layerIndex, mipmap.width, mipmap.height, 1, glFormat, layerData );
+ }
+ texture.clearLayerUpdates();
+ } else {
+ state.compressedTexSubImage3D( WebGL.TEXTURE_2D_ARRAY, i, 0, 0, 0, mipmap.width, mipmap.height, image.depth, glFormat, mipmap.data );
+ }
+ } else {
+ state.compressedTexImage3D( WebGL.TEXTURE_2D_ARRAY, i, glInternalFormat, mipmap.width, mipmap.height, image.depth, 0, mipmap.data);//, 0, 0 );
+ }
+ } else {
+ console.warning( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()' );
+ }
+ } else {
+ if ( useTexStorage && dataReady) {
+ state.texSubImage3D( WebGL.TEXTURE_2D_ARRAY, i, 0, 0, 0, mipmap.width, mipmap.height, image.depth, glFormat, glType, mipmap.data );
+ } else {
+ state.texImage3D( WebGL.TEXTURE_2D_ARRAY, i, glInternalFormat, mipmap.width, mipmap.height, image.depth, 0, glFormat, glType, mipmap.data );
+ }
+ }
+ }
+ } else {
+ if ( useTexStorage && allocateMemory ) {
+ state.texStorage2D( WebGL.TEXTURE_2D, levels, glInternalFormat, mipmaps[ 0 ].width, mipmaps[ 0 ].height );
+ }
+
+ for (int i = 0, il = mipmaps.length; i < il; i ++ ) {
+ mipmap = mipmaps[ i ];
+
+ if ( texture.format != RGBAFormat ) {
+ if ( glFormat > 0 ) {
+ if ( useTexStorage && dataReady) {
+ state.compressedTexSubImage2D( WebGL.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, mipmap.data );
+ } else {
+ state.compressedTexImage2D( WebGL.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data );
+ }
+ } else {
+ console.warning( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()' );
+ }
+ } else {
+ if ( useTexStorage && dataReady) {
+ state.texSubImage2D( WebGL.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data );
+ } else {
+ state.texImage2D( WebGL.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data );
+ }
+ }
+ }
+ }
+ }
+ else if (texture is DataArrayTexture) {
+ if (useTexStorage) {
+ if (allocateMemory) {
+ state.texStorage3D(WebGL.TEXTURE_2D_ARRAY, levels, glInternalFormat, image.width, image.height, image.depth);
+ }
+ if(dataReady){
+ if ( texture.layerUpdates.isNotEmpty ) {
+ final layerByteLength = TextureUtils.getByteLength( image.width, image.height, texture.format, texture.type );
+ for ( final layerIndex in texture.layerUpdates ) {
+ final layerData = image.data.subarray(
+ layerIndex * layerByteLength / image.data.BYTES_PER_ELEMENT,
+ ( layerIndex + 1 ) * layerByteLength / image.data.BYTES_PER_ELEMENT
+ );
+ state.texSubImage3D( WebGL.TEXTURE_2D_ARRAY, 0, 0, 0, layerIndex, image.width, image.height, 1, glFormat, glType, layerData );
+ }
+
+ texture.clearLayerUpdates();
+ }
+ else {
+ state.texSubImage3D( WebGL.TEXTURE_2D_ARRAY, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data );
+ }
+ }
+ } else {
+ state.texImage3D(WebGL.TEXTURE_2D_ARRAY, 0, glInternalFormat, image.width, image.height, image.depth, 0,
+ glFormat, glType, image.data);
+ }
+ }
+ else if (texture is Data3DTexture) {
+ if (useTexStorage) {
+ if (allocateMemory) {
+ state.texStorage3D(WebGL.TEXTURE_3D, levels, glInternalFormat, image.width, image.height, image.depth);
+ }
+ if(dataReady){
+ state.texSubImage3D(WebGL.TEXTURE_3D, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data);
+ }
+ } else {
+ state.texImage3D(WebGL.TEXTURE_3D, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat,glType, image.data);
+ }
+ }
+ else if (texture is FramebufferTexture) {
+ if (allocateMemory) {
+ if (useTexStorage) {
+ state.texStorage2D(WebGL.TEXTURE_2D, levels, glInternalFormat, image.width, image.height);
+ } else{
+ int width = image.width, height = image.height;
+
+ for (int i = 0; i < levels; i++) {
+ state.texImage2D(WebGL.TEXTURE_2D, i, glInternalFormat, width, height, 0, glFormat, glType, null);
+
+ width >>= 1;
+ height >>= 1;
+ }
+ }
+ }
+ }
+ else {
+ // regular Texture (image, video, canvas)
+
+ // use manually created mipmaps if available
+ // if there are no manual mipmaps
+ // set 0 level mipmap and then use GL to generate other mipmap levels
+
+ if (mipmaps.isNotEmpty) {
+ if (useTexStorage && allocateMemory) {
+ state.texStorage2D(WebGL.TEXTURE_2D, levels, glInternalFormat, mipmaps[0].width, mipmaps[0].height);
+ }
+
+ for (int i = 0, il = mipmaps.length; i < il; i++) {
+ mipmap = mipmaps[i];
+ if (useTexStorage && dataReady) {
+ state.texSubImage2DIf(WebGL.TEXTURE_2D, i, 0, 0, glFormat, glType, mipmap);
+ }
+ else {
+ state.texImage2DIf(WebGL.TEXTURE_2D, i, glInternalFormat, glFormat, glType, mipmap);
+ }
+ }
+
+ texture.generateMipmaps = false;
+ }
+ else {
+ if (useTexStorage) {
+ if (allocateMemory) {
+ state.texStorage2D(WebGL.TEXTURE_2D, levels, glInternalFormat, image.width.toInt(), image.height.toInt());
+ }
+ if(dataReady){
+ state.texSubImage2DIf(WebGL.TEXTURE_2D, 0, 0, 0, glFormat, glType, image);
+ }
+ }
+ else {
+ state.texImage2DIf(WebGL.TEXTURE_2D, 0, glInternalFormat, glFormat, glType, image);
+ }
+ }
+ }
+
+ if (textureNeedsGenerateMipmaps(texture)) {
+ generateMipmap(textureType);
+ }
+
+ source.currentVersion = source.version;
+
+ if (texture.onUpdate != null) texture.onUpdate!(texture);
+ }
+
+ textureProperties["__version"] = texture.version;
+ }
+
+ void uploadCubeTexture(Map textureProperties, Texture texture, int slot) {
+ if (texture.image.length != 6) return;
+
+ final forceUpload = initTexture(textureProperties, texture);
+ final source = texture.source;
+
+ state.activeTexture(WebGL.TEXTURE0 + slot);
+ state.bindTexture(WebGL.TEXTURE_CUBE_MAP, textureProperties['__webglTexture']);
+
+ if (source.version != source.currentVersion || forceUpload) {
+ state.activeTexture(WebGL.TEXTURE0 + slot);
+
+
+ _gl.pixelStorei(WebGL.UNPACK_ALIGNMENT, texture.unpackAlignment);
+ if (kIsWeb) {
+ final workingPrimaries = ColorManagement.getPrimaries( ColorManagement.workingColorSpace );
+ final texturePrimaries = texture.colorSpace == NoColorSpace ? null : ColorManagement.getPrimaries( ColorSpace.fromString(texture.colorSpace) );
+ final unpackConversion = texture.colorSpace == NoColorSpace || workingPrimaries == texturePrimaries ? WebGL.NONE : WebGL.BROWSER_DEFAULT_WEBGL;
+
+ _gl.pixelStorei(WebGL.UNPACK_FLIP_Y_WEBGL, texture.flipY ? 1 : 0);
+ _gl.pixelStorei(WebGL.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha ? 1 : 0);
+ _gl.pixelStorei(WebGL.UNPACK_COLORSPACE_CONVERSION_WEBGL, unpackConversion);
+ }
+
+ final isCompressed = (texture.isCompressedTexture || texture is CompressedTexture);
+ final isDataTexture = (texture.image[0] != null && texture is DataTexture);
+ final isCubeTexture = (texture.image[0] != null && texture is CubeTexture);
+ print('here1');
+ final cubeImage = [];
+
+ for (int i = 0; i < 6; i++) {
+ if (!isCompressed && !isDataTexture) {
+ cubeImage.add(texture.image[i]);
+ }
+ else {
+ print('here2');
+ cubeImage.add(isDataTexture ? texture.image[i].image : texture.image[i]);
+ }
+
+ cubeImage[i] = verifyColorSpace(texture, cubeImage[i]);
+ }
+
+ final image = cubeImage[0],
+ glFormat = utils.convert(texture.format, texture.colorSpace),
+ glType = utils.convert(texture.type),
+ glInternalFormat = getInternalFormat(texture.internalFormat, glFormat, glType, texture.colorSpace);
+
+ final useTexStorage = (isWebGL2 && texture is! VideoTexture);
+ final allocateMemory = (textureProperties['__version'] == null);
+ final dataReady = source.dataReady;
+ int levels = getMipLevels(texture, image);
+
+ setTextureParameters(WebGL.TEXTURE_CUBE_MAP, texture);
+
+ dynamic mipmaps;
+
+ if (isCompressed) {
+ if (useTexStorage && allocateMemory) {
+ state.texStorage2D(WebGL.TEXTURE_CUBE_MAP, levels, glInternalFormat, image.width, image.height);
+ }
+
+ for (int i = 0; i < 6; i++) {
+ mipmaps = cubeImage[i].mipmaps;
+
+ for (int j = 0; j < mipmaps.length; j++) {
+ final mipmap = mipmaps[j];
+
+ if (texture.format != RGBAFormat) {
+ if (glFormat != null) {
+ if (useTexStorage && dataReady) {
+ state.compressedTexSubImage2D(WebGL.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, 0, 0, mipmap.width, mipmap.height, glFormat, mipmap.data);
+ }
+ else {
+ state.compressedTexImage2D(WebGL.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data);
+ }
+ }
+ else {
+ console.warning('WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()');
+ }
+ }
+ else {
+ if (useTexStorage && dataReady) {
+ state.texSubImage2D(WebGL.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, 0, 0, mipmap.width, mipmap.height, glFormat,glType, mipmap.data);
+ }
+ else {
+ state.texImage2D(WebGL.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glInternalFormat, mipmap.width, mipmap.height,0, glFormat, glType, mipmap.data);
+ }
+ }
+ }
+ }
+ }
+ else {
+ mipmaps = texture.mipmaps;
+
+ if (useTexStorage && allocateMemory) {
+ // TODO: Uniformly handle mipmap definitions
+ // Normal textures and compressed cube textures define base level + mips with their mipmap array
+ // Uncompressed cube textures use their mipmap array only for mips (no base level)
+
+ if (mipmaps.length > 0) levels++;
+
+ state.texStorage2D(WebGL.TEXTURE_CUBE_MAP, levels, glInternalFormat, cubeImage[0].width, cubeImage[0].height);
+ }
+
+ for (int i = 0; i < 6; i++) {
+ if (isDataTexture || isCubeTexture) {
+ if (useTexStorage && dataReady) {
+ if( kIsWeb ){
+ state.texSubImage2DNoSize(WebGL.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, glFormat, glType, cubeImage[i].data);
+ }
+ else{
+ state.texSubImage2D(WebGL.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, cubeImage[i].width, cubeImage[i].height, glFormat, glType, cubeImage[i].data);
+ }
+ }
+ else {
+ if( kIsWeb ){
+ state.texImage2DNoSize(WebGL.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, glFormat, glType, cubeImage[i].data);
+ }
+ else{
+ state.texImage2D(WebGL.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, cubeImage[i].width,cubeImage[i].height, 0, glFormat, glType, cubeImage[i].data);
+ }
+ }
+
+ for (int j = 0; j < mipmaps.length; j++) {
+ final mipmap = mipmaps[j];
+ final mipmapImage = mipmap.image[i].image;
+
+ if (useTexStorage && dataReady) {
+ state.texSubImage2D(WebGL.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, 0, 0, mipmapImage.width,mipmapImage.height, glFormat, glType, mipmapImage.data);
+ }
+ else {
+ state.texImage2D(WebGL.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, glInternalFormat, mipmapImage.width,mipmapImage.height, 0, glFormat, glType, mipmapImage.data);
+ }
+ }
+ }
+ else {
+ if (useTexStorage && dataReady) {
+ state.texSubImage2DIf(WebGL.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, glFormat, glType, cubeImage[i]);
+ }
+ else {
+ state.texImage2DIf(WebGL.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, glFormat, glType, cubeImage[i]);
+ }
+
+ for (int j = 0; j < mipmaps.length; j++) {
+ final mipmap = mipmaps[j];
+
+ if (useTexStorage && dataReady) {
+ state.texSubImage2DIf(WebGL.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, 0, 0, glFormat, glType, mipmap.image[i]);
+ }
+ else {
+ state.texImage2DIf(WebGL.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, glInternalFormat, glFormat, glType, mipmap.image[i]);
+ }
+ }
+ }
+ }
+ }
+
+ if (textureNeedsGenerateMipmaps(texture)) {
+ // We assume images for cube map have the same size.
+ generateMipmap(WebGL.TEXTURE_CUBE_MAP);
+ }
+
+ source.currentVersion = source.version;
+
+ if (texture.onUpdate != null) texture.onUpdate!(texture);
+ }
+
+ textureProperties['__version'] = texture.version;
+ }
+
+ // Render targets
+
+ // Setup storage for target texture and bind it to correct framebuffer
+ void setupFrameBufferTexture(framebuffer, RenderTarget renderTarget, Texture texture, attachment, textureTarget, level ) {
+ final glFormat = utils.convert( texture.format, texture.colorSpace );
+ final glType = utils.convert( texture.type );
+ final glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace );
+ final renderTargetProperties = properties.get( renderTarget );
+ final textureProperties = properties.get( texture );
+
+ textureProperties['__renderTarget'] = renderTarget;
+
+ if (renderTargetProperties['__hasExternalTextures'] == null) {
+
+ final width = math.max( 1, renderTarget.width >> level );
+ final height = math.max( 1, renderTarget.height >> level );
+
+ if ( textureTarget == WebGL.TEXTURE_3D || textureTarget == WebGL.TEXTURE_2D_ARRAY ) {
+ state.texImage3D( textureTarget, level, glInternalFormat, width, height, renderTarget.depth, 0, glFormat, glType, null );
+ } else {
+ state.texImage2D( textureTarget, level, glInternalFormat, width, height, 0, glFormat, glType, null );
+ }
+ }
+
+ state.bindFramebuffer( WebGL.FRAMEBUFFER, framebuffer );
+
+ if ( useMultisampledRTT( renderTarget ) && multisampledRTTExt != null) {
+ multisampledRTTExt.framebufferTexture2DMultisampleEXT( WebGL.FRAMEBUFFER, attachment, textureTarget, textureProperties['__webglTexture'], 0, getRenderTargetSamples( renderTarget ) );
+ }
+ else if ( textureTarget == WebGL.TEXTURE_2D || ( textureTarget >= WebGL.TEXTURE_CUBE_MAP_POSITIVE_X && textureTarget <= WebGL.TEXTURE_CUBE_MAP_NEGATIVE_Z ) ) { // see #24753
+ _gl.framebufferTexture2D( WebGL.FRAMEBUFFER, attachment, textureTarget, textureProperties['__webglTexture'], level );
+ }
+
+ state.bindFramebuffer( WebGL.FRAMEBUFFER, null );
+ }
+
+ // Setup storage for internal depth/stencil buffers and bind to correct framebuffer
+ void setupRenderBufferStorage(Renderbuffer renderbuffer, RenderTarget renderTarget, bool isMultisample) {
+ _gl.bindRenderbuffer( WebGL.RENDERBUFFER, renderbuffer );
+
+ if ( renderTarget.depthBuffer ) {
+ // retrieve the depth attachment types
+ final depthTexture = renderTarget.depthTexture;
+ final depthType = depthTexture != null && depthTexture.isDepthTexture ? depthTexture.type : null;
+ final glInternalFormat = getInternalDepthFormat( renderTarget.stencilBuffer, depthType );
+ final glAttachmentType = renderTarget.stencilBuffer ? WebGL.DEPTH_STENCIL_ATTACHMENT : WebGL.DEPTH_ATTACHMENT;
+
+ // set up the attachment
+ final samples = getRenderTargetSamples( renderTarget );
+ final isUseMultisampledRTT = useMultisampledRTT( renderTarget );
+ if ( isUseMultisampledRTT && multisampledRTTExt != null) {
+ multisampledRTTExt.renderbufferStorageMultisampleEXT( WebGL.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height );
+ }
+ else if ( isMultisample ) {
+ _gl.renderbufferStorageMultisample( WebGL.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height );
+ } else {
+ _gl.renderbufferStorage( WebGL.RENDERBUFFER, glInternalFormat, renderTarget.width, renderTarget.height );
+ }
+
+ _gl.framebufferRenderbuffer( WebGL.FRAMEBUFFER, glAttachmentType, WebGL.RENDERBUFFER, renderbuffer );
+ } else {
+ final textures = renderTarget.textures;
+
+ for (int i = 0; i < textures.length; i ++ ) {
+ final texture = textures[ i ];
+
+ final glFormat = utils.convert( texture.format, texture.colorSpace );
+ final glType = utils.convert( texture.type );
+ final glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace );
+ final samples = getRenderTargetSamples( renderTarget );
+
+ if ( isMultisample && useMultisampledRTT( renderTarget ) == false ) {
+ _gl.renderbufferStorageMultisample( WebGL.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height );
+ } else if ( useMultisampledRTT( renderTarget ) ) {
+ multisampledRTTExt.renderbufferStorageMultisampleEXT( WebGL.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height );
+ } else {
+ _gl.renderbufferStorage( WebGL.RENDERBUFFER, glInternalFormat, renderTarget.width, renderTarget.height );
+ }
+ }
+ }
+
+ _gl.bindRenderbuffer( WebGL.RENDERBUFFER, null );
+ }
+
+
+ // Setup resources for a Depth Texture for a FBO (needs an extension)
+ void setupDepthTexture(framebuffer, RenderTarget renderTarget) {
+ final renderTargetProperties = properties.get( renderTarget );
+ final isCube = renderTarget is CubeRenderTarget;
+
+ // if the bound depth texture has changed
+ if ( renderTargetProperties['__boundDepthTexture'] != renderTarget.depthTexture ) {
+ // fire the dispose event to get rid of stored state associated with the previously bound depth buffer
+ final depthTexture = renderTarget.depthTexture;
+ if ( renderTargetProperties['__depthDisposeCallback'] != null) {
+ renderTargetProperties['__depthDisposeCallback']();
+ }
+
+ // set up dispose listeners to track when the currently attached buffer is implicitly unbound
+ if ( depthTexture != null) {
+ disposeEvent(){
+ renderTargetProperties.remove('__boundDepthTexture');//delete renderTargetProperties.__boundDepthTexture;
+ renderTargetProperties.remove('__depthDisposeCallback');//delete renderTargetProperties.__depthDisposeCallback;
+ depthTexture.removeEventListener( 'dispose', disposeEvent );
+ };
+
+ depthTexture.addEventListener( 'dispose', disposeEvent );
+ renderTargetProperties['__depthDisposeCallback'] = disposeEvent;
+ }
+ renderTargetProperties['__boundDepthTexture'] = depthTexture;
+ }
+
+ if ( renderTarget.depthTexture != null && ! renderTargetProperties['__autoAllocateDepthBuffer'] ) {
+ if ( isCube ) throw( 'target.depthTexture not supported in Cube render targets' );
+ setupDepthTexture( renderTargetProperties['__webglFramebuffer'], renderTarget );
+ } else {
+ if ( isCube ) {
+ renderTargetProperties['__webglDepthbuffer'] = [];
+
+ for (int i = 0; i < 6; i ++ ) {
+ state.bindFramebuffer( WebGL.FRAMEBUFFER, renderTargetProperties['__webglFramebuffer'][ i ] );
+
+ if ( renderTargetProperties['__webglDepthbuffer'][ i ] == null ) {
+ renderTargetProperties['__webglDepthbuffer'][ i ] = _gl.createRenderbuffer();
+ setupRenderBufferStorage( renderTargetProperties['__webglDepthbuffer'][ i ], renderTarget, false );
+ } else {
+ // attach buffer if it's been created already
+ final glAttachmentType = renderTarget.stencilBuffer? WebGL.DEPTH_STENCIL_ATTACHMENT : WebGL.DEPTH_ATTACHMENT;
+ final renderbuffer = renderTargetProperties['__webglDepthbuffer'][ i ];
+ _gl.bindRenderbuffer( WebGL.RENDERBUFFER, renderbuffer );
+ _gl.framebufferRenderbuffer( WebGL.FRAMEBUFFER, glAttachmentType, WebGL.RENDERBUFFER, renderbuffer );
+ }
+ }
+ } else {
+ state.bindFramebuffer( WebGL.FRAMEBUFFER, renderTargetProperties['__webglFramebuffer'] );
+
+ if ( renderTargetProperties['__webglDepthbuffer'] == null ) {
+ renderTargetProperties['__webglDepthbuffer'] = _gl.createRenderbuffer();
+ setupRenderBufferStorage( renderTargetProperties['__webglDepthbuffer'], renderTarget, false );
+ } else {
+ // attach buffer if it's been created already
+ final glAttachmentType = renderTarget.stencilBuffer ? WebGL.DEPTH_STENCIL_ATTACHMENT : WebGL.DEPTH_ATTACHMENT;
+ final renderbuffer = renderTargetProperties['__webglDepthbuffer'];
+ _gl.bindRenderbuffer( WebGL.RENDERBUFFER, renderbuffer );
+ _gl.framebufferRenderbuffer( WebGL.FRAMEBUFFER, glAttachmentType, WebGL.RENDERBUFFER, renderbuffer );
+ }
+ }
+ }
+
+ state.bindFramebuffer( WebGL.FRAMEBUFFER, null );
+ }
+
+ // Setup GL resources for a non-texture depth buffer
+ void setupDepthRenderbuffer(RenderTarget renderTarget) {
+ final renderTargetProperties = properties.get( renderTarget );
+ final isCube = ( renderTarget is CubeRenderTarget == true );
+
+ // if the bound depth texture has changed
+ if ( renderTargetProperties['__boundDepthTexture'] != renderTarget.depthTexture ) {
+ // fire the dispose event to get rid of stored state associated with the previously bound depth buffer
+ final depthTexture = renderTarget.depthTexture;
+ if ( renderTargetProperties['__depthDisposeCallback'] ) {
+ renderTargetProperties['__depthDisposeCallback']();
+ }
+
+ // set up dispose listeners to track when the currently attached buffer is implicitly unbound
+ if ( depthTexture != null) {
+ disposeEvent(){
+ renderTargetProperties.remove('__boundDepthTexture');//delete renderTargetProperties.__boundDepthTexture;
+ renderTargetProperties.remove('__depthDisposeCallback');//delete renderTargetProperties.__depthDisposeCallback;
+ depthTexture.removeEventListener( 'dispose', disposeEvent );
+ }
+ depthTexture.addEventListener( 'dispose', disposeEvent );
+ renderTargetProperties['__depthDisposeCallback'] = disposeEvent;
+ }
+ renderTargetProperties['__boundDepthTexture'] = depthTexture;
+ }
+
+ if ( renderTarget.depthTexture != null && !renderTargetProperties['__autoAllocateDepthBuffer'] ) {
+ if ( isCube ) throw( 'target.depthTexture not supported in Cube render targets' );
+ setupDepthTexture( renderTargetProperties['__webglFramebuffer'], renderTarget );
+ } else {
+ if (isCube) {
+ renderTargetProperties['__webglDepthbuffer'] = [];
+
+ for (int i = 0; i < 6; i++) {
+ state.bindFramebuffer( WebGL.FRAMEBUFFER, renderTargetProperties['__webglFramebuffer'][ i ] );
+
+ if (renderTargetProperties['__webglDepthbuffer'].length <= i || renderTargetProperties['__webglDepthbuffer'][ i ] == null ) {
+ (renderTargetProperties['__webglDepthbuffer'] as List).listSetter(i,_gl.createRenderbuffer());// [ i ] = _gl.createRenderbuffer();
+ setupRenderBufferStorage( renderTargetProperties['__webglDepthbuffer'][ i ], renderTarget, false );
+ } else {
+ // attach buffer if it's been created already
+ final glAttachmentType = renderTarget.stencilBuffer ? WebGL.DEPTH_STENCIL_ATTACHMENT : WebGL.DEPTH_ATTACHMENT;
+ final renderbuffer = renderTargetProperties['__webglDepthbuffer'][ i ];
+ _gl.bindRenderbuffer( WebGL.RENDERBUFFER, renderbuffer );
+ _gl.framebufferRenderbuffer( WebGL.FRAMEBUFFER, glAttachmentType, WebGL.RENDERBUFFER, renderbuffer );
+ }
+ }
+ } else {
+ state.bindFramebuffer( WebGL.FRAMEBUFFER, renderTargetProperties['__webglFramebuffer'] );
+
+ if ( renderTargetProperties['__webglDepthbuffer'] == null ) {
+ renderTargetProperties['__webglDepthbuffer'] = _gl.createRenderbuffer();
+ setupRenderBufferStorage( renderTargetProperties['__webglDepthbuffer'], renderTarget, false );
+ } else {
+ // attach buffer if it's been created already
+ final glAttachmentType = renderTarget.stencilBuffer ? WebGL.DEPTH_STENCIL_ATTACHMENT : WebGL.DEPTH_ATTACHMENT;
+ final renderbuffer = renderTargetProperties['__webglDepthbuffer'];
+ _gl.bindRenderbuffer( WebGL.RENDERBUFFER, renderbuffer );
+ _gl.framebufferRenderbuffer( WebGL.FRAMEBUFFER, glAttachmentType, WebGL.RENDERBUFFER, renderbuffer );
+ }
+ }
+ }
+
+ state.bindFramebuffer( WebGL.FRAMEBUFFER, null );
+ }
+
+ // rebind framebuffer with external textures
+ void rebindTextures(RenderTarget renderTarget, dynamic colorTexture, dynamic depthTexture) {
+ final renderTargetProperties = properties.get(renderTarget);
+
+ if (colorTexture != null) {
+ setupFrameBufferTexture(renderTargetProperties["__webglFramebuffer"], renderTarget, renderTarget.texture,
+ WebGL.COLOR_ATTACHMENT0, WebGL.TEXTURE_2D,0);
+ }
+
+ if (depthTexture != null) {
+ setupDepthRenderbuffer(renderTarget);
+ }
+ }
+
+ // Set up GL resources for the render target
+ void setupRenderTarget(RenderTarget renderTarget) {
+ final texture = renderTarget.texture;
+
+ final renderTargetProperties = properties.get( renderTarget );
+ final textureProperties = properties.get( texture );
+
+ renderTarget.addEventListener( 'dispose', onRenderTargetDispose );
+
+ final textures = renderTarget.textures;
+
+ final isCube = renderTarget is CubeRenderTarget;
+ final isMultipleRenderTargets = ( textures.length > 1 );
+
+ if ( ! isMultipleRenderTargets ) {
+ if ( textureProperties['__webglTexture'] == null ) {
+ textureProperties['__webglTexture'] = _gl.createTexture();
+ }
+
+ textureProperties['__version'] = texture.version;
+ info.memory['textures'] = info.memory['textures']! + 1;
+ }
+
+ // Setup framebuffer
+
+ if ( isCube ) {
+ renderTargetProperties['__webglFramebuffer'] = [];
+ for ( int i = 0; i < 6; i ++ ) {
+ if (texture.mipmaps.isNotEmpty ) {
+ renderTargetProperties['__webglFramebuffer'][ i ] = [];
+ for ( int level = 0; level < texture.mipmaps.length; level ++ ) {
+ renderTargetProperties['__webglFramebuffer'][ i ][ level ] = _gl.createFramebuffer();
+ }
+ } else {
+ (renderTargetProperties['__webglFramebuffer'] as List).listSetter(i, _gl.createFramebuffer());
+ }
+ }
+ } else {
+ if ( texture.mipmaps.isNotEmpty ) {
+ renderTargetProperties['__webglFramebuffer'] = [];
+ for ( int level = 0; level < texture.mipmaps.length; level ++ ) {
+ renderTargetProperties['__webglFramebuffer'][ level ] = _gl.createFramebuffer();
+ }
+ } else {
+ renderTargetProperties['__webglFramebuffer'] = _gl.createFramebuffer();
+ }
+
+ if ( isMultipleRenderTargets ) {
+ for ( int i = 0, il = textures.length; i < il; i ++ ) {
+ final attachmentProperties = properties.get( textures[ i ] );
+ if ( attachmentProperties['__webglTexture'] == null ) {
+ attachmentProperties['__webglTexture'] = _gl.createTexture();
+ info.memory['textures'] = info.memory['textures']! +1;;
+ }
+ }
+ }
+
+ if ( ( renderTarget.samples > 0 ) && useMultisampledRTT( renderTarget ) == false ) {
+ renderTargetProperties['__webglMultisampledFramebuffer'] = _gl.createFramebuffer();
+ renderTargetProperties['__webglColorRenderbuffer'] = [];
+
+ state.bindFramebuffer( WebGL.FRAMEBUFFER, renderTargetProperties['__webglMultisampledFramebuffer'] );
+
+ for ( int i = 0; i < textures.length; i ++ ) {
+ final texture = textures[ i ];
+ (renderTargetProperties['__webglColorRenderbuffer'] as List).listSetter(i, _gl.createRenderbuffer());
+
+ _gl.bindRenderbuffer( WebGL.RENDERBUFFER, renderTargetProperties['__webglColorRenderbuffer'][ i ] );
+
+ final glFormat = utils.convert( texture.format, texture.colorSpace );
+ final glType = utils.convert( texture.type );
+ final glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace, renderTarget.isXRRenderTarget == true );
+ final samples = getRenderTargetSamples( renderTarget );
+
+ _gl.renderbufferStorageMultisample( WebGL.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height );
+ _gl.framebufferRenderbuffer( WebGL.FRAMEBUFFER, WebGL.COLOR_ATTACHMENT0 + i, WebGL.RENDERBUFFER, renderTargetProperties['__webglColorRenderbuffer'][ i ] );
+ }
+
+ _gl.bindRenderbuffer( WebGL.RENDERBUFFER, null );
+
+ if ( renderTarget.depthBuffer ) {
+ renderTargetProperties['__webglDepthRenderbuffer'] = _gl.createRenderbuffer();
+ setupRenderBufferStorage( renderTargetProperties['__webglDepthRenderbuffer'], renderTarget, true );
+ }
+
+ state.bindFramebuffer( WebGL.FRAMEBUFFER, null );
+ }
+ }
+
+ // Setup color buffer
+
+ if ( isCube ) {
+ state.bindTexture( WebGL.TEXTURE_CUBE_MAP, textureProperties['__webglTexture'] );
+ setTextureParameters( WebGL.TEXTURE_CUBE_MAP, texture );
+
+ for ( int i = 0; i < 6; i ++ ) {
+ if (texture.mipmaps.isNotEmpty ) {
+ for ( int level = 0; level < texture.mipmaps.length; level ++ ) {
+ setupFrameBufferTexture( renderTargetProperties['__webglFramebuffer'][ i ][ level ], renderTarget, texture, WebGL.COLOR_ATTACHMENT0, WebGL.TEXTURE_CUBE_MAP_POSITIVE_X + i, level );
+ }
+ } else {
+ setupFrameBufferTexture( renderTargetProperties['__webglFramebuffer'][ i ], renderTarget, texture, WebGL.COLOR_ATTACHMENT0, WebGL.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0 );
+ }
+ }
+
+ if ( textureNeedsGenerateMipmaps( texture ) ) {
+ generateMipmap( WebGL.TEXTURE_CUBE_MAP );
+ }
+
+ state.unbindTexture();
+
+ } else if ( isMultipleRenderTargets ) {
+ for ( int i = 0, il = textures.length; i < il; i ++ ) {
+ final attachment = textures[ i ];
+ final attachmentProperties = properties.get( attachment );
+
+ state.bindTexture( WebGL.TEXTURE_2D, attachmentProperties['__webglTexture'] );
+ setTextureParameters( WebGL.TEXTURE_2D, attachment );
+ setupFrameBufferTexture( renderTargetProperties['__webglFramebuffer'], renderTarget, attachment, WebGL.COLOR_ATTACHMENT0 + i, WebGL.TEXTURE_2D, 0 );
+
+ if ( textureNeedsGenerateMipmaps( attachment ) ) {
+ generateMipmap( WebGL.TEXTURE_2D );
+ }
+ }
+
+ state.unbindTexture();
+ } else {
+ int glTextureType = WebGL.TEXTURE_2D;
+
+ if ( renderTarget is Angle3DRenderTarget || renderTarget is AngleArrayRenderTarget ) {
+ glTextureType = renderTarget is Angle3DRenderTarget ? WebGL.TEXTURE_3D : WebGL.TEXTURE_2D_ARRAY;
+ }
+
+ state.bindTexture( glTextureType, textureProperties['__webglTexture'] );
+ setTextureParameters( glTextureType, texture );
+
+ if (texture.mipmaps.isNotEmpty ) {
+ for ( int level = 0; level < texture.mipmaps.length; level ++ ) {
+ setupFrameBufferTexture( renderTargetProperties['__webglFramebuffer'][ level ], renderTarget, texture, WebGL.COLOR_ATTACHMENT0, glTextureType, level );
+ }
+ } else {
+ setupFrameBufferTexture( renderTargetProperties['__webglFramebuffer'], renderTarget, texture, WebGL.COLOR_ATTACHMENT0, glTextureType, 0 );
+ }
+
+ if ( textureNeedsGenerateMipmaps( texture ) ) {
+ generateMipmap( glTextureType );
+ }
+ state.unbindTexture();
+ }
+
+
+ if ( renderTarget.depthBuffer ) {
+ setupDepthRenderbuffer( renderTarget );
+ }
+ }
+
+ void updateRenderTargetMipmap(RenderTarget renderTarget) {
+ final textures = renderTarget.textures;
+ for (int i = 0, il = textures.length; i < il; i++) {
+ final texture = textures[i];
+
+ if (textureNeedsGenerateMipmaps(texture)) {
+ final target = renderTarget is CubeRenderTarget ? WebGL.TEXTURE_CUBE_MAP : WebGL.TEXTURE_2D;
+ final webglTexture = properties.get(texture)["__webglTexture"];
+
+ state.bindTexture(target, webglTexture);
+ generateMipmap(target);
+ state.bindTexture(target, null);
+ }
+ }
+ }
+
+ final Uint32List invalidationArrayDraw = Uint32List(1);
+ final Uint32List invalidationArrayRead = Uint32List(1);
+
+ void updateMultisampleRenderTarget(RenderTarget renderTarget) {
+ if ( renderTarget.samples > 0 ) {
+ if ( !useMultisampledRTT( renderTarget )) {
+ final textures = renderTarget.textures;
+ final width = renderTarget.width;
+ final height = renderTarget.height;
+ int mask = WebGL.COLOR_BUFFER_BIT;
+ final depthStyle = renderTarget.stencilBuffer ? WebGL.DEPTH_STENCIL_ATTACHMENT : WebGL.DEPTH_ATTACHMENT;
+ final renderTargetProperties = properties.get( renderTarget );
+ final isMultipleRenderTargets = ( textures.length > 1 );
+
+ // If MRT we need to remove FBO attachments
+ if ( isMultipleRenderTargets ) {
+ for (int i = 0; i < textures.length; i ++ ) {
+ state.bindFramebuffer( WebGL.FRAMEBUFFER, renderTargetProperties['__webglMultisampledFramebuffer'] );
+ _gl.framebufferRenderbuffer( WebGL.FRAMEBUFFER, WebGL.COLOR_ATTACHMENT0 + i, WebGL.RENDERBUFFER, null );
+
+ state.bindFramebuffer( WebGL.FRAMEBUFFER, renderTargetProperties['__webglFramebuffer'] );
+ _gl.framebufferTexture2D( WebGL.DRAW_FRAMEBUFFER, WebGL.COLOR_ATTACHMENT0 + i, WebGL.TEXTURE_2D, null, 0 );
+ }
+ }
+
+ state.bindFramebuffer( WebGL.READ_FRAMEBUFFER, renderTargetProperties['__webglMultisampledFramebuffer'] );
+ state.bindFramebuffer( WebGL.DRAW_FRAMEBUFFER, renderTargetProperties['__webglFramebuffer'] );
+
+ for (int i = 0; i < textures.length; i ++ ) {
+ if ( renderTarget.resolveDepthBuffer ) {
+ if ( renderTarget.depthBuffer ) mask |= WebGL.DEPTH_BUFFER_BIT;
+ if ( renderTarget.stencilBuffer && renderTarget.resolveStencilBuffer ) mask |= WebGL.STENCIL_BUFFER_BIT;
+ }
+
+ if ( isMultipleRenderTargets ) {
+ _gl.framebufferRenderbuffer( WebGL.READ_FRAMEBUFFER, WebGL.COLOR_ATTACHMENT0, WebGL.RENDERBUFFER, renderTargetProperties['__webglColorRenderbuffer'][ i ] );
+
+ final webglTexture = properties.get( textures[ i ] )['__webglTexture'];
+ _gl.framebufferTexture2D( WebGL.DRAW_FRAMEBUFFER, WebGL.COLOR_ATTACHMENT0, WebGL.TEXTURE_2D, webglTexture, 0 );
+ }
+
+ _gl.blitFramebuffer( 0, 0, width, height, 0, 0, width, height, mask, WebGL.NEAREST );
+
+ if ( supportsInvalidateFramebuffer) {
+ invalidationArrayRead.length = 0;
+ invalidationArrayDraw.length = 0;
+
+ invalidationArrayRead.add( WebGL.COLOR_ATTACHMENT0 + i );
+
+ if ( renderTarget.depthBuffer && !renderTarget.resolveDepthBuffer) {
+ invalidationArrayRead.add( depthStyle );
+ invalidationArrayDraw.add( depthStyle );
+
+ _gl.invalidateFramebuffer( WebGL.DRAW_FRAMEBUFFER, invalidationArrayDraw );
+ }
+
+ _gl.invalidateFramebuffer( WebGL.READ_FRAMEBUFFER, invalidationArrayRead );
+ }
+ }
+
+ state.bindFramebuffer( WebGL.READ_FRAMEBUFFER, null );
+ state.bindFramebuffer( WebGL.DRAW_FRAMEBUFFER, null );
+
+ // If MRT since pre-blit we removed the FBO we need to reconstruct the attachments
+ if ( isMultipleRenderTargets ) {
+ for (int i = 0; i < textures.length; i ++ ) {
+ state.bindFramebuffer( WebGL.FRAMEBUFFER, renderTargetProperties['__webglMultisampledFramebuffer'] );
+ _gl.framebufferRenderbuffer( WebGL.FRAMEBUFFER, WebGL.COLOR_ATTACHMENT0 + i, WebGL.RENDERBUFFER, renderTargetProperties['__webglColorRenderbuffer'][ i ] );
+
+ final webglTexture = properties.get( textures[ i ] )['__webglTexture'];
+
+ state.bindFramebuffer( WebGL.FRAMEBUFFER, renderTargetProperties['__webglFramebuffer'] );
+ _gl.framebufferTexture2D( WebGL.DRAW_FRAMEBUFFER, WebGL.COLOR_ATTACHMENT0 + i, WebGL.TEXTURE_2D, webglTexture, 0 );
+ }
+ }
+
+ state.bindFramebuffer( WebGL.DRAW_FRAMEBUFFER, renderTargetProperties['__webglMultisampledFramebuffer'] );
+ } else {
+ if ( renderTarget.depthBuffer && !renderTarget.resolveDepthBuffer && supportsInvalidateFramebuffer ) {
+ depthStyle[0] = renderTarget.stencilBuffer ? WebGL.DEPTH_STENCIL_ATTACHMENT : WebGL.DEPTH_ATTACHMENT;
+ _gl.invalidateFramebuffer( WebGL.DRAW_FRAMEBUFFER, depthStyle );
+ }
+ }
+ }
+ }
+
+ Uint32List depthStyle = Uint32List.fromList([WebGL.DEPTH_ATTACHMENT]);
+
+ bool useMultisampledRenderToTexture(RenderTarget renderTarget) {
+ final renderTargetProperties = properties.get(renderTarget);
+
+ return isWebGL2 &&
+ renderTarget.samples > 0 &&
+ extensions.has('WEBGL_multisampled_render_to_texture') == true &&
+ renderTargetProperties["__useRenderToTexture"] != false;
+ }
+
+ int getRenderTargetSamples(RenderTarget renderTarget) {
+ return math.min(maxSamples, renderTarget.samples);
+ }
+
+ bool useMultisampledRTT(RenderTarget renderTarget) {
+ final renderTargetProperties = properties.get(renderTarget);
+
+ return renderTarget.samples > 0 &&
+ extensions.has('WEBGL_multisampled_render_to_texture') &&
+ renderTargetProperties["__useRenderToTexture"] != false;
+ }
+
+ void updateVideoTexture(VideoTexture texture) {
+ final frame = info.render["frame"];
+
+ // Check the last frame we updated the VideoTexture
+
+ if (_videoTextures[texture] != frame) {
+ _videoTextures[texture] = frame;
+ texture.update();
+ }
+ }
+
+ void uploadOpenGLTexture(Map textureProperties, OpenGLTexture texture, int slot) {
+ final frame = info.render["frame"];
+ if (_videoTextures[texture] != frame) {
+ _videoTextures[texture] = frame;
+ texture.update();
+ }
+
+ const textureType = WebGL.TEXTURE_2D;
+
+ initTexture(textureProperties, texture);
+
+ state.activeTexture(WebGL.TEXTURE0 + slot);
+ state.bindTexture(textureType, textureProperties["__webglTexture"]);
+ if( kIsWeb ){
+ gl.pixelStorei(WebGL.UNPACK_FLIP_Y_WEBGL, texture.flipY ? 1 : 0);
+ gl.pixelStorei(WebGL.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha ? 1 : 0);
+ }
+ gl.pixelStorei(WebGL.UNPACK_ALIGNMENT, texture.unpackAlignment);
+ }
+
+ verifyColorSpace(Texture texture, image) {
+ final colorSpace = texture.colorSpace;
+ final format = texture.format;
+ final type = texture.type;
+
+ if ( texture.isCompressedTexture || texture is VideoTexture ) return image;
+
+ if ( colorSpace != LinearSRGBColorSpace && colorSpace != NoColorSpace ) {
+
+ // sRGB
+
+ if ( ColorManagement.getTransfer( ColorSpace.fromString( colorSpace)) == SRGBTransfer ) {
+
+ // in WebGL 2 uncompressed textures can only be sRGB encoded if they have the RGBA8 format
+
+ if ( format != RGBAFormat || type != UnsignedByteType ) {
+ console.warning( 'THREE.WebGLTextures: sRGB encoded textures have to use RGBAFormat and UnsignedByteType.' );
+ }
+ } else {
+ console.error( 'THREE.WebGLTextures: Unsupported texture color space: $colorSpace');
+ }
+ }
+
+ return image;
+ }
+
+ void dispose(){
+ if(_didDispose) return;
+ _didDispose = true;
+ extensions.dispose();
+ state.dispose();
+ properties.dispose();
+ capabilities.dispose();
+ utils.dispose();
+ info.dispose();
+ _videoTextures.clear();
+ _sources.clear();
+ wrappingToGL.clear();
+ filterToGL.clear();
+ }
+}
diff --git a/packages/three_js_angle_renderer/lib/angle/angle_uniforms.dart b/packages/three_js_angle_renderer/lib/angle/angle_uniforms.dart
new file mode 100755
index 00000000..d118ef00
--- /dev/null
+++ b/packages/three_js_angle_renderer/lib/angle/angle_uniforms.dart
@@ -0,0 +1,107 @@
+/*
+ * Uniforms of a program.
+ * Those form a tree structure with a special top-level container for the root,
+ * which you get by calling 'new WebGLUniforms( gl, program )'.
+ *
+ *
+ * Properties of inner nodes including the top-level container:
+ *
+ * .seq - array of nested uniforms
+ * .map - nested uniforms by name
+ *
+ *
+ * Methods of all nodes except the top-level container:
+ *
+ * .setValue( gl, value, [textures] )
+ *
+ * uploads a uniform value(s)
+ * the 'textures' parameter is needed for sampler uniforms
+ *
+ *
+ * Static methods of the top-level container (textures factorizations):
+ *
+ * .upload( gl, seq, values, textures )
+ *
+ * sets uniforms in 'seq' to 'values[id].value'
+ *
+ * .seqWithValue( seq, values ) : filteredSeq
+ *
+ * filters 'seq' entries with corresponding entry in values
+ *
+ *
+ * Methods of the top-level container (textures factorizations):
+ *
+ * .setValue( gl, name, value, textures )
+ *
+ * sets uniform with name 'name' to 'value'
+ *
+ * .setOptional( gl, obj, prop )
+ *
+ * like .set for an optional property of the object
+ *
+ */
+
+// Root Container
+
+part of three_webgl;
+
+class AngleUniforms with AngleUniform {
+ bool _didDispose = false;
+ RenderingContext gl;
+ AngleProgram program;
+
+ AngleUniforms(this.gl, this.program) {
+ seq = [];
+ map = {};
+
+ final n = gl.getProgramParameter(program.program!, WebGL.ACTIVE_UNIFORMS);
+
+ for (int i = 0; i < n.id; ++i) {
+ final info = gl.getActiveUniform(program.program!, i);
+ final addr = gl.getUniformLocation(program.program!, info.name);
+ parseUniform(info, addr, this);
+ }
+ }
+
+ void dispose(){
+ if(_didDispose) return;
+ _didDispose = true;
+ program.dispose();
+ }
+
+ void setValue(RenderingContext gl, String name, dynamic value, [AngleTextures? textures]) {
+ final u = map[name];
+ if (u != null) u.setValue(gl, value, textures);
+ }
+
+ void setOptional(RenderingContext gl, object, String name) {
+ final v = object.getValue(name);
+ if (v != null) setValue(gl, name, v);
+ }
+
+ static void upload(RenderingContext gl, List seq, Map values, [AngleTextures? textures]) {
+ for (int i = 0, n = seq.length; i != n; ++i) {
+ final u = seq[i];
+ final v = values[u.id];
+ if (v["needsUpdate"] != false) {
+ // note: always updating when .needsUpdate is null
+ u.setValue(gl, v["value"], textures);
+ }
+ }
+ }
+
+ static List seqWithValue(List seq, Map values) {
+ List r = [];
+ for (int i = 0, n = seq.length; i != n; ++i) {
+ final u = seq[i];
+ // print("seqWithValue u.id: ${u.id} ");
+
+ if (values.keys.contains(u.id)) {
+ r.add(u);
+ } else {
+ // print("seqWithValue u.id: ${u.id} is not add ");
+ }
+ }
+ return r;
+ }
+}
diff --git a/packages/three_js_angle_renderer/lib/angle/angle_uniforms_groups.dart b/packages/three_js_angle_renderer/lib/angle/angle_uniforms_groups.dart
new file mode 100644
index 00000000..24238b8d
--- /dev/null
+++ b/packages/three_js_angle_renderer/lib/angle/angle_uniforms_groups.dart
@@ -0,0 +1,278 @@
+part of three_webgl;
+
+class AngleUniformsGroups{
+ AngleState state;
+ AngleCapabilities capabilities;
+ AngleInfo info;
+
+ Map buffers = {};
+ Map updateList = {};
+ late final Uint32List allocatedBindingPoints = Uint32List(maxBindingPoints);
+ RenderingContext gl;
+
+ late final int maxBindingPoints; // binding points are global whereas block indices are per shader program
+
+ AngleUniformsGroups(this.gl, this.info, this.capabilities, this.state ){
+ maxBindingPoints = gl.getParameter( WebGL.MAX_UNIFORM_BUFFER_BINDINGS );
+ }
+
+ void bind(UniformsGroup uniformsGroup, AngleProgram? program ) {
+ final webglProgram = program?.program;
+ state.uniformBlockBinding( uniformsGroup, webglProgram );
+ }
+
+ void update(uniformsGroup, AngleProgram? program ) {
+ dynamic buffer = buffers[uniformsGroup.id];
+
+ if ( buffer == null ) {
+ prepareUniformsGroup( uniformsGroup );
+ buffer = createBuffer( uniformsGroup );
+ buffers[ uniformsGroup.id ] = buffer;
+ uniformsGroup.addEventListener( 'dispose', onUniformsGroupsDispose );
+ }
+
+ // ensure to update the binding points/block indices mapping for this program
+
+ final webglProgram = program?.program;
+ state.updateUBOMapping( uniformsGroup, webglProgram! );
+
+ // update UBO once per frame
+
+ final frame = info.render['frame'];
+
+ if ( updateList[ uniformsGroup.id ] != frame ) {
+ updateBufferData( uniformsGroup );
+ updateList[ uniformsGroup.id ] = frame;
+ }
+ }
+
+ Buffer createBuffer(UniformsGroup uniformsGroup ) {
+ final bindingPointIndex = allocateBindingPointIndex();
+ uniformsGroup.bindingPointIndex = bindingPointIndex;
+
+ final buffer = gl.createBuffer();
+ final size = uniformsGroup.size;
+ final usage = uniformsGroup.usage;
+
+ gl.bindBuffer( WebGL.UNIFORM_BUFFER, buffer );
+ gl.bufferData( WebGL.UNIFORM_BUFFER, size!, usage);
+ gl.bindBuffer( WebGL.UNIFORM_BUFFER, null );
+ gl.bindBufferBase( WebGL.UNIFORM_BUFFER, bindingPointIndex, buffer );
+
+ return buffer;
+ }
+
+ int allocateBindingPointIndex() {
+ for (int i = 0; i < maxBindingPoints; i ++ ) {
+ if (allocatedBindingPoints[i] == 0) {
+ allocatedBindingPoints[i] = 1;
+ return i;
+ }
+ }
+
+ console.error( 'THREE.WebGLRenderer: Maximum number of simultaneously usable uniforms groups reached.' );
+ return 0;
+ }
+
+ void updateBufferData(Map uniformsGroup ) {
+ final buffer = buffers[ uniformsGroup['id'] ];
+ final uniforms = uniformsGroup['uniforms'];
+ final cache = uniformsGroup['__cache'];
+
+ gl.bindBuffer( WebGL.UNIFORM_BUFFER, buffer );
+
+ for (int i = 0, il = uniforms.length; i < il; i ++ ) {
+ final uniformArray = uniforms[ i ] is List? uniforms[ i ] : [ uniforms[ i ] ];
+
+ for (int j = 0, jl = uniformArray.length; j < jl; j ++ ) {
+ final uniform = uniformArray[ j ];
+
+ if ( hasUniformChanged( uniform, i, j, cache ) == true ) {
+ final offset = uniform['__offset'];
+ final values = uniform['value'] is List? uniform['value'] : [ uniform['value'] ];
+ int arrayOffset = 0;
+
+ for (int k = 0; k < values.length; k ++ ) {
+ final value = values[ k ];
+ final info = getUniformSize( value );
+
+ // TODO add integer and struct support
+ if (value is double || value is int || value is num || value is bool ) {
+ uniform['__data'][ 0 ] = value;
+ gl.bufferSubData( WebGL.UNIFORM_BUFFER, offset + arrayOffset, uniform['__data'] );
+ }
+ else if ( value is Matrix3 ) {
+ uniform['__data'][ 0 ] = value.storage[ 0 ];
+ uniform['__data'][ 1 ] = value.storage[ 1 ];
+ uniform['__data'][ 2 ] = value.storage[ 2 ];
+ uniform['__data'][ 3 ] = 0;
+ uniform['__data'][ 4 ] = value.storage[ 3 ];
+ uniform['__data'][ 5 ] = value.storage[ 4 ];
+ uniform['__data'][ 6 ] = value.storage[ 5 ];
+ uniform['__data'][ 7 ] = 0;
+ uniform['__data'][ 8 ] = value.storage[ 6 ];
+ uniform['__data'][ 9 ] = value.storage[ 7 ];
+ uniform['__data'][ 10 ] = value.storage[ 8 ];
+ uniform['__data'][ 11 ] = 0;
+ }
+ else {
+ value.toArray( uniform['__data'], arrayOffset );
+ arrayOffset += info['storage']! ~/ Float32List.bytesPerElement;
+ }
+ }
+
+ gl.bufferSubData( WebGL.UNIFORM_BUFFER, offset, uniform['__data'] );
+ }
+ }
+ }
+
+ gl.bindBuffer( WebGL.UNIFORM_BUFFER, null );
+ }
+
+ bool hasUniformChanged( uniform, int index, indexArray, cache ) {
+ final value = uniform['value'];
+ final indexString = '${index}_$indexArray';
+
+ if ( cache[ indexString ] == null ) {
+ if (value is double || value is int || value is num || value is bool ) {
+ cache[ indexString ] = value;
+ } else {
+ cache[ indexString ] = value.clone();
+ }
+
+ return true;
+ }
+ else {
+ final cachedObject = cache[ indexString ];
+
+ // compare current value with cached entry
+
+ if (value is double || value is int || value is num || value is bool ) {
+ if ( cachedObject != value ) {
+ cache[ indexString ] = value;
+ return true;
+ }
+ }
+ else {
+ if ( cachedObject.equals( value ) == false ) {
+ cachedObject.copy( value );
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ AngleUniformsGroups prepareUniformsGroup(UniformsGroup uniformsGroup ) {
+ // determine total buffer size according to the STD140 layout
+ // Hint: STD140 is the only supported layout in WebGL 2
+ final uniforms = uniformsGroup.uniforms;
+
+ int offset = 0; // global buffer offset in bytes
+ const int chunkSize = 16; // size of a chunk in bytes
+
+ for (int i = 0, l = uniforms.length; i < l; i ++ ) {
+ final List uniformArray = [uniforms[ i ]];
+
+ for (int j = 0, jl = uniformArray.length; j < jl; j ++ ) {
+ final uniform = uniformArray[ j ];
+ final values = uniform.value is List ? uniform.value : [ uniform.value ];
+
+ for (int k = 0, kl = values.length; k < kl; k ++ ) {
+ final value = values[ k ];
+ final info = getUniformSize( value );
+ // Calculate the chunk offset
+ final chunkOffset = offset % chunkSize;
+ final chunkPadding = chunkOffset % info['boundary']!; // required padding to match boundary
+ final chunkStart = chunkOffset + chunkPadding; // the start position in the current chunk for the data
+
+ offset += chunkPadding;
+
+ // Check for chunk overflow
+ if ( chunkStart != 0 && ( chunkSize - chunkStart ) < info['storage']! ) {
+ // Add padding and adjust offset
+ offset += ( chunkSize - chunkStart );
+ }
+
+ // the following two properties will be used for partial buffer updates
+ uniform.data = Float32List( info['storage']! ~/ Float32List.bytesPerElement);
+ uniform.offset = offset;
+
+ // Update the global offset
+ offset += info['storage']!;
+ }
+ }
+ }
+
+ // ensure correct final padding
+
+ final chunkOffset = offset % chunkSize;
+
+ if ( chunkOffset > 0 ) offset += ( chunkSize - chunkOffset );
+
+ uniformsGroup.size = offset;
+ uniformsGroup.cache = {};
+
+ return this;
+ }
+
+ Map getUniformSize( value ) {
+ final Map info = {
+ 'boundary': 0, // bytes
+ 'storage': 0 // bytes
+ };
+
+ // determine sizes according to STD140
+
+ if (value is double || value is int || value is num || value is bool ) {
+ info['boundary'] = 4;
+ info['storage'] = 4;
+ } else if ( value is Vector2 ) {
+ info['boundary'] = 8;
+ info['storage'] = 8;
+ } else if ( value is Vector3 || value is Color ) {
+ info['boundary'] = 16;
+ info['storage'] = 12; // evil: vec3 must start on a 16-byte boundary but it only consumes 12 bytes
+ } else if ( value is Vector4 ) {
+ info['boundary'] = 16;
+ info['storage'] = 16;
+ } else if ( value is Matrix3 ) {
+ info['boundary'] = 48;
+ info['storage'] = 48;
+ } else if ( value is Matrix4 ) {
+ info['boundary'] = 64;
+ info['storage'] = 64;
+ } else if ( value is Texture ) {
+ console.warning( 'THREE.WebGLRenderer: Texture samplers can not be part of an uniforms group.' );
+ } else {
+ console.warning( 'THREE.WebGLRenderer: Unsupported uniform value type. $value');
+ }
+
+ return info;
+ }
+
+ void onUniformsGroupsDispose( event ) {
+ final uniformsGroup = event.target;
+
+ uniformsGroup.removeEventListener( 'dispose', onUniformsGroupsDispose );
+
+ final index = allocatedBindingPoints.indexOf( uniformsGroup['__bindingPointIndex'] );
+ allocatedBindingPoints.removeAt(index);
+
+ gl.deleteBuffer( buffers[ uniformsGroup.id ] );
+
+ buffers.remove(uniformsGroup.id);
+ updateList.remove(uniformsGroup.id);
+ }
+
+ void dispose() {
+ for ( final id in buffers.keys ) {
+ gl.deleteBuffer( buffers[ id ] );
+ }
+
+ allocatedBindingPoints.clear();
+ buffers = {};
+ updateList = {};
+ }
+}
diff --git a/packages/three_js_angle_renderer/lib/angle/angle_uniforms_helper.dart b/packages/three_js_angle_renderer/lib/angle/angle_uniforms_helper.dart
new file mode 100755
index 00000000..24f93a6e
--- /dev/null
+++ b/packages/three_js_angle_renderer/lib/angle/angle_uniforms_helper.dart
@@ -0,0 +1,756 @@
+part of three_webgl;
+
+final emptyTexture = Texture();
+final emptyArrayTexture = DataArrayTexture();
+final empty3dTexture = Data3DTexture();
+final emptyCubeTexture = CubeTexture();
+
+// --- Utilities ---
+
+// Array Caches (provide typed arrays for temporary by size)
+
+Map arrayCacheF32 = {};
+Map arrayCacheI32 = {};
+
+// Float32List caches used for uploading Matrix uniforms
+
+final mat4array = Float32List(16);
+final mat3array = Float32List(9);
+final mat2array = Float32List(4);
+
+// --- Uniform Classes ---
+
+class SingleUniform with AngleUniformsHelper {
+ late Function setValue;
+ late int type;
+ late ActiveInfo activeInfo;
+
+ SingleUniform(id, this.activeInfo, UniformLocation addr) {
+ this.id = id;
+ this.addr = addr;
+ cache = {};
+ type = activeInfo.type;
+ setValue = getSingularSetter(activeInfo.type);
+ }
+}
+
+class PureArrayUniform with AngleUniformsHelper {
+ late Function setValue;
+ late int type;
+ late ActiveInfo activeInfo;
+
+ PureArrayUniform(id, this.activeInfo, addr) {
+ this.id = id;
+ this.addr = addr;
+ cache = {};
+ size = activeInfo.size;
+ type = activeInfo.type;
+ setValue = getPureArraySetter(activeInfo.type);
+ }
+
+ void updateCache(data) {
+ final cache = this.cache;
+ copyArray(cache, data);
+ }
+}
+
+mixin AngleUniform {
+ late List seq;
+ late Map map;
+}
+
+class StructuredUniform with AngleUniformsHelper, AngleUniform {
+ StructuredUniform(id) {
+ this.id = id;
+ seq = [];
+ map = {};
+ }
+
+ void setValue(gl, value, textures) {
+ final seq = this.seq;
+
+ for (int i = 0, n = seq.length; i != n; ++i) {
+ final u = seq[i];
+ u.setValue(gl, value[u.id], textures);
+ }
+ }
+}
+
+// --- Top-level ---
+
+// Parser - builds up the property tree from the path strings
+
+final rePathPart = RegExp(r"(\w+)(\])?(\[|\.)?"); //g;
+
+// extracts
+// - the identifier (member name or array index)
+// - followed by an optional right bracket (found when array index)
+// - followed by an optional left bracket or dot (type of subscript)
+//
+// Note: These portions can be read in a non-overlapping fashion and
+// allow straightforward parsing of the hierarchy that WebGL encodes
+// in the uniform names.
+
+void addUniform(AngleUniform container, uniformObject) {
+ container.seq.add(uniformObject);
+ container.map[uniformObject.id] = uniformObject;
+}
+
+void parseUniform(ActiveInfo activeInfo, UniformLocation addr, AngleUniform container) {
+ final path = activeInfo.name;
+ final pathLength = path.length;
+
+ //console.info("WebGLUniformsHelper.parseUniform path: $path addr: ${addr.id} ");
+
+ // reset RegExp object, because of the early exit of a previous run
+ // RePathPart.lastIndex = 0;
+
+ final matches = rePathPart.allMatches(path);
+
+ for (final match in matches) {
+ dynamic id = match.group(1);
+ final idIsIndex = match.group(2) == ']';
+ final subscript = match.group(3);
+
+ if (idIsIndex) id = int.tryParse(id) ?? 0; // convert to integer
+
+ final matchEnd = match.end;
+
+ if (subscript == null || subscript == '[' && matchEnd + 2 == pathLength) {
+ // bare name or "pure" bottom-level array "[0]" suffix
+ addUniform(container, subscript == null ? SingleUniform(id, activeInfo, addr) : PureArrayUniform(id, activeInfo, addr));
+ break;
+ }
+ else {
+ // step into inner node / create it in case it doesn't exist
+
+ final map = container.map;
+ StructuredUniform? next = map[id];
+
+ if (next == null) {
+ next = StructuredUniform(id);
+ addUniform(container, next);
+ }
+
+ container = next;
+ }
+ }
+}
+
+mixin AngleUniformsHelper {
+ // Flattening for arrays of vectors and matrices
+ // id string || int
+ late dynamic id;
+ Map cache = {};
+ UniformLocation addr = UniformLocation(0);
+ late int size;
+
+ void dispose(){
+ cache.clear();
+ }
+
+ Float32List flat = Float32List(0);
+
+ Float32List flatten(List? array, int nBlocks, int blockSize) {
+ if(array == null || array.isEmpty) return Float32List(0);
+ final firstElem = array[0];
+
+ if (firstElem is num || firstElem is double || firstElem is int) {
+ List array2 = [];
+
+ for (final element in array) {
+ array2.add(element.toDouble());
+ }
+
+ return Float32List.fromList(array2);
+ }
+
+ final n = nBlocks * blockSize;
+ Float32List? r = arrayCacheF32[n];
+
+ if (r == null) {
+ r = Float32List(n);
+ arrayCacheF32[n] = r;
+ }
+
+ if (nBlocks != 0) {
+ for (int i = 0; i < nBlocks; i++) {
+ List data = array[i].storage.toList();
+
+ data.asMap().forEach((index, element) {
+ int idx = i * blockSize + index;
+ r![idx] = element.toDouble();
+ });
+ }
+ }
+ return r;
+ }
+
+ bool arraysEqual(Map a, b) {
+ if (a.keys.length != b.length) return false;
+
+ for (int i = 0, l = a.keys.length; i < l; i++) {
+ if (a[i] != b[i]) return false;
+ }
+
+ return true;
+ }
+
+ void copyArray(Map a, b) {
+ final l = b.length;
+ a.clear();
+ for (int i = 0; i < l; i++) {
+ a[i] = b[i];
+ }
+ }
+
+ // Texture unit allocation
+
+ Int32List allocTexUnits(textures, n) {
+ Int32List? r = arrayCacheI32[n];
+
+ if (r == null) {
+ r = Int32List(n);
+ arrayCacheI32[n] = r;
+ }
+
+ for (int i = 0; i != n; ++i) {
+ r[i] = textures.allocateTextureUnit();
+ }
+
+ return r;
+ }
+
+ // --- Setters ---
+
+ // Note: Defining these methods externally, because they come in a bunch
+ // and this way their names minify.
+
+ // Single scalar
+
+ void setValueV1f(RenderingContext gl, v, [AngleTextures? textures]) {
+ final cache = this.cache;
+
+ if (cache[0] == v) return;
+ gl.uniform1f(addr, v.toDouble());
+
+ cache[0] = v;
+ }
+
+ // Single float vector (from flat array or three.VectorN)
+
+ void setValueV2f(RenderingContext gl, v, [AngleTextures? textures]) {
+ final cache = this.cache;
+
+ if (v.x != null) {
+ if (cache[0] != v.x || cache[1] != v.y) {
+ gl.uniform2f(addr, v.x, v.y);
+
+ cache[0] = v.x;
+ cache[1] = v.y;
+ }
+ } else {
+ if (arraysEqual(cache, v)) return;
+
+ gl.uniform2fv(addr, v);
+
+ copyArray(cache, v);
+ }
+ }
+
+ void setValueV3f(RenderingContext gl, v, [AngleTextures? textures]) {
+ final cache = this.cache;
+
+ if (v is Vector3) {
+ if (cache[0] != v.x || cache[1] != v.y || cache[2] != v.z) {
+ gl.uniform3f(addr, v.x, v.y, v.z);
+
+ cache[0] = v.x;
+ cache[1] = v.y;
+ cache[2] = v.z;
+ }
+ } else if (v is Color) {
+ double cacheR = 0;
+ double cacheG = 0;
+ double cacheB = 0;
+
+ if (cache.length >= 3) {
+ cacheR = cache[0];
+ cacheG = cache[1];
+ cacheB = cache[2];
+ }
+
+ if (cacheR != v.red || cacheG != v.green || cacheB != v.blue) {
+ gl.uniform3f(addr, v.red, v.green, v.blue);
+
+ cache[0] = v.red;
+ cache[1] = v.green;
+ cache[2] = v.blue;
+ }
+ } else {
+ if (arraysEqual(cache, v)) return;
+ gl.uniform3fv(addr, Float32List.fromList(v));
+
+ copyArray(cache, v);
+ }
+ }
+
+ void setValueV4f(RenderingContext gl, v, [AngleTextures? textures]) {
+ final cache = this.cache;
+
+ if (v is Vector4) {
+ if (cache[0] != v.x || cache[1] != v.y || cache[2] != v.z || cache[3] != v.w) {
+ gl.uniform4f(addr, v.x, v.y, v.z, v.w);
+
+ cache[0] = v.x;
+ cache[1] = v.y;
+ cache[2] = v.z;
+ cache[3] = v.w;
+ }
+ } else if (v is Color) {
+ if (cache[0] != v.red || cache[1] != v.green || cache[2] != v.blue || cache[3] != 1.0) {
+ gl.uniform4f(addr, v.red, v.green, v.blue, 1.0);
+
+ cache[0] = v.red.toDouble();
+ cache[1] = v.green.toDouble();
+ cache[2] = v.blue.toDouble();
+ cache[3] = 1.0;
+ }
+ } else if (v is List) {
+ if (cache[0] != v[0] || cache[1] != v[1] || cache[2] != v[2] || cache[3] != v[3]) {
+ gl.uniform4f(addr, v[0], v[1], v[2], v[3]);
+
+ cache[0] = v[0];
+ cache[1] = v[1];
+ cache[2] = v[2];
+ cache[3] = v[3];
+ }
+ } else {
+ if (arraysEqual(cache, v)) return;
+
+ gl.uniform4fv(addr, v);
+
+ copyArray(cache, v);
+ }
+ }
+
+ // Single matrix (from flat array or three.MatrixN)
+
+ void setValueM2(RenderingContext gl, v, [AngleTextures? textures]) {
+ final cache = this.cache;
+ final elements = v?.storage;
+
+ if (elements == null) {
+ if (arraysEqual(cache, v)) return;
+
+ gl.uniformMatrix2fv(addr, false, v);
+
+ copyArray(cache, v);
+ } else {
+ if (arraysEqual(cache, elements)) return;
+
+ //mat2array.set(List.from(elements.map((e) => e.toDouble())), 0);
+
+ gl.uniformMatrix2fv(addr, false, mat2array);
+ copyArray(cache, elements);
+ }
+ }
+
+ void setValueM3(RenderingContext gl, Matrix3? v, [AngleTextures? textures]) {
+ final cache = this.cache;
+ final elements = v?.storage;
+
+ if (elements == null) {
+ if (arraysEqual(cache, v)) return;
+
+ gl.uniformMatrix3fv(addr, false, elements!);
+
+ copyArray(cache, v);
+ }
+ else {
+ if (arraysEqual(cache, elements)) {
+ return;
+ }
+
+ gl.uniformMatrix3fv(addr, false, elements);
+ copyArray(cache, elements);
+ }
+ }
+
+ void setValueM4(RenderingContext gl, Matrix4 v, [AngleTextures? textures]) {
+ final cache = this.cache;
+ final elements = v.storage;
+
+ if (arraysEqual(cache, elements)) {
+ return;
+ }
+ gl.uniformMatrix4fv(addr, false, elements);
+ copyArray(cache, elements);
+ }
+
+ // Single texture (2D / Cube)
+
+ void setValueT1(RenderingContext gl, Texture? v, AngleTextures textures) {
+ final cache = this.cache;
+ final unit = textures.allocateTextureUnit();
+
+ if (cache[0] != unit) {
+ gl.uniform1i(addr, unit);
+ cache[0] = unit;
+ }
+
+ textures.setTexture2D(v ?? emptyTexture, unit);
+ }
+
+ void setValueT2DArray1(RenderingContext gl,Texture? v, AngleTextures textures) {
+ final cache = this.cache;
+ final unit = textures.allocateTextureUnit();
+
+ if (cache[0] != unit) {
+ gl.uniform1i(addr, unit);
+ cache[0] = unit;
+ }
+
+ textures.setTexture2DArray(v ?? emptyArrayTexture, unit);
+ }
+
+ void setValueT3D1(RenderingContext gl,Texture? v, [AngleTextures? textures]) {
+ final cache = this.cache;
+ final unit = textures!.allocateTextureUnit();
+
+ if (cache[0] != unit) {
+ gl.uniform1i(addr, unit);
+ cache[0] = unit;
+ }
+
+ textures.setTexture3D(v ?? empty3dTexture, unit);
+ }
+
+ void setValueT6(RenderingContext gl,Texture? v, [AngleTextures? textures]) {
+ final cache = this.cache;
+ final unit = textures!.allocateTextureUnit();
+
+ if (cache[0] != unit) {
+ gl.uniform1i(addr, unit);
+ cache[0] = unit;
+ }
+
+ textures.setTextureCube(v ?? emptyCubeTexture, unit);
+ }
+
+ // Integer / Boolean vectors or arrays thereof (always flat arrays)
+
+ void setValueV1i(RenderingContext gl, v, [AngleTextures? textures]) {
+ final cache = this.cache;
+
+ if (cache[0] == v) return;
+
+ if (v is bool) {
+ if (v) {
+ gl.uniform1i(addr, 1);
+ } else {
+ gl.uniform1i(addr, 0);
+ }
+ } else {
+ gl.uniform1i(addr, v.toInt());
+ }
+
+ cache[0] = v;
+ }
+ Int32List iv = Int32List(2);
+
+ void setValueV2i(RenderingContext gl, Vector v, [AngleTextures? textures]) {
+ final cache = this.cache;
+ if (arraysEqual(cache, v)) return;
+ iv[0] = v.x.toInt();
+ iv[1] = v.y.toInt();
+ gl.uniform2iv(addr, iv);
+ copyArray(cache, v.copyIntoArray());
+ }
+
+ void setValueV3i(RenderingContext gl, v, [AngleTextures? textures]) {
+ final cache = this.cache;
+ if (arraysEqual(cache, v)) return;
+ gl.uniform3iv(addr, v);
+ copyArray(cache, v);
+ }
+
+ void setValueV4i(RenderingContext gl, v, [AngleTextures? textures]) {
+ final cache = this.cache;
+
+ if (arraysEqual(cache, v)) return;
+
+ gl.uniform4iv(addr, v);
+
+ copyArray(cache, v);
+ }
+
+ // uint
+
+ void setValueV1ui(RenderingContext gl, v, [AngleTextures? textures]) {
+ final cache = this.cache;
+
+ if (cache[0] == v) return;
+ gl.uniform1ui(addr, v);
+ cache[0] = v;
+ }
+
+ void setValueV2ui(RenderingContext gl, v, [AngleTextures? textures]) {
+ final cache = this.cache;
+
+ if (arraysEqual(cache, v)) return;
+ gl.uniform2uiv(addr, v);
+ copyArray(cache, v);
+ }
+
+ void setValueV3ui(RenderingContext gl, v, [AngleTextures? textures]) {
+ final cache = this.cache;
+
+ if (arraysEqual(cache, v)) return;
+ gl.uniform3uiv(addr, v);
+ copyArray(cache, v);
+ }
+
+ void setValueV4ui(RenderingContext gl, v, [AngleTextures? textures]) {
+ final cache = this.cache;
+
+ if (arraysEqual(cache, v)) return;
+ gl.uniform4uiv(addr, v);
+ copyArray(cache, v);
+ }
+
+ // Helper to pick the right setter for the singular case
+
+ Function getSingularSetter(int type) {
+ switch (type) {
+ case 0x1406: return setValueV1f; // FLOAT
+ case 0x8b50: return setValueV2f; // _VEC2
+ case 0x8b51: return setValueV3f; // _VEC3
+ case 0x8b52: return setValueV4f; // _VEC4
+
+ case 0x8b5a: return setValueM2; // _MAT2
+ case 0x8b5b: return setValueM3; // _MAT3
+ case 0x8b5c: return setValueM4; // _MAT4
+
+ case 0x1404: case 0x8b56: return setValueV1i; // INT, BOOL
+ case 0x8b53: case 0x8b57: return setValueV2i; // _VEC2
+ case 0x8b54: case 0x8b58: return setValueV3i; // _VEC3
+ case 0x8b55: case 0x8b59: return setValueV4i; // _VEC4
+
+ case 0x1405: return setValueV1ui; // UINT
+ case 0x8dc6: return setValueV2ui; // _VEC2
+ case 0x8dc7: return setValueV3ui; // _VEC3
+ case 0x8dc8: return setValueV4ui; // _VEC4
+
+ case 0x8b5e: // SAMPLER_2D
+ case 0x8d66: // SAMPLER_EXTERNAL_OES
+ case 0x8dca: // INT_SAMPLER_2D
+ case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D
+ case 0x8b62: // SAMPLER_2D_SHADOW
+ return setValueT1;
+
+ case 0x8b5f: // SAMPLER_3D
+ case 0x8dcb: // INT_SAMPLER_3D
+ case 0x8dd3: // UNSIGNED_INT_SAMPLER_3D
+ return setValueT3D1;
+
+ case 0x8b60: // SAMPLER_CUBE
+ case 0x8dcc: // INT_SAMPLER_CUBE
+ case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE
+ case 0x8dc5: // SAMPLER_CUBE_SHADOW
+ return setValueT6;
+
+ case 0x8dc1: // SAMPLER_2D_ARRAY
+ case 0x8dcf: // INT_SAMPLER_2D_ARRAY
+ case 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY
+ case 0x8dc4: // SAMPLER_2D_ARRAY_SHADOW
+ return setValueT2DArray1;
+ default:
+ throw ("getSingularSetter id: $id type: $type");
+ }
+ }
+
+ // Array of scalars
+ void setValueV1fArray(RenderingContext gl, v, [AngleTextures? textures]) {
+ final data = flatten(v, size, 1);
+ gl.uniform1fv(addr, data);
+ }
+
+ // Integer / Boolean vectors or arrays thereof (always flat arrays)
+ void setValueV1iArray(RenderingContext gl, v, [AngleTextures? textures]) {
+ gl.uniform1iv(addr, v);
+ }
+
+ void setValueV2iArray(RenderingContext gl, v, [AngleTextures? textures]) {
+ gl.uniform2iv(addr, v);
+ }
+
+ void setValueV3iArray(RenderingContext gl, v, [AngleTextures? textures]) {
+ gl.uniform3iv(addr, v);
+ }
+
+ void setValueV4iArray(RenderingContext gl, v, [AngleTextures? textures]) {
+ gl.uniform4iv(addr, v);
+ }
+
+ // Array of vectors (flat or from THREE classes)
+
+ void setValueV2fArray(RenderingContext gl, v, [AngleTextures? textures]) {
+ final data = flatten(v, size, 2);
+ gl.uniform2fv(addr, data);
+ }
+
+ void setValueV3fArray(RenderingContext gl, v, [AngleTextures? textures]) {
+ final data = flatten(v, size, 3);
+ gl.uniform3fv(addr, data);
+ }
+
+ void setValueV4fArray(RenderingContext gl, v, [AngleTextures? textures]) {
+ final data = flatten(v, size, 4);
+ gl.uniform4fv(addr, data);
+ }
+
+ // Array of matrices (flat or from THREE clases)
+
+ void setValueM2Array(RenderingContext gl, v, [AngleTextures? textures]) {
+ final data = flatten(v, size, 4);
+ gl.uniformMatrix2fv(addr, false, data);
+ }
+
+ void setValueM3Array(RenderingContext gl, v, [AngleTextures? textures]) {
+ final data = flatten(v, size, 9);
+ gl.uniformMatrix3fv(addr, false, data);
+ }
+
+ void setValueM4Array(RenderingContext gl, v, [AngleTextures? textures]) {
+ final data = flatten(v, size, 16);
+ gl.uniformMatrix4fv(addr, false, data);
+ }
+
+ // Array of unsigned integer
+
+ void setValueV1uiArray(RenderingContext gl, v, [AngleTextures? textures]) {
+ gl.uniform1uiv(addr, v);
+ }
+
+ // Array of unsigned integer vectors (from flat array)
+
+ void setValueV2uiArray(RenderingContext gl, v, [AngleTextures? textures]) {
+ gl.uniform2uiv(addr, v);
+ }
+
+ void setValueV3uiArray(RenderingContext gl, v, [AngleTextures? textures]) {
+ gl.uniform3uiv(addr, v);
+ }
+
+ void setValueV4uiArray(RenderingContext gl, v, [AngleTextures? textures]) {
+ gl.uniform4uiv(addr, v);
+ }
+
+ // Array of textures (2D / 3D / Cube / 2DArray)
+ void setValueT1Array(RenderingContext gl, v, [AngleTextures? textures]) {
+ final n = v.length;
+
+ final units = allocTexUnits(textures, n);
+
+ if (!arraysEqual(cache, units)){
+ gl.uniform1iv(addr, units);
+ copyArray(cache, units);
+ }
+
+ for (int i = 0; i != n; ++i) {
+ textures?.setTexture2D(v[i] ?? emptyTexture, units[i]);
+ }
+ }
+
+ void setValueT3DArray(RenderingContext gl, v, [AngleTextures? textures]) {
+ final n = v.length;
+
+ final units = allocTexUnits(textures, n);
+
+ gl.uniform1iv(addr, units);
+
+ for (int i = 0; i != n; ++i) {
+ textures?.setTexture3D(v[i] ?? empty3dTexture, units[i]);
+ }
+ }
+
+ void setValueT6Array(RenderingContext gl, v, [AngleTextures? textures]) {
+ final n = v.length;
+
+ final units = allocTexUnits(textures, n);
+
+ if (!arraysEqual(cache, units)){
+ gl.uniform1iv(addr, units);
+ copyArray(cache, units);
+ }
+
+ for (int i = 0; i != n; ++i) {
+ textures?.setTextureCube(v[i] ?? emptyCubeTexture, units[i]);
+ }
+ }
+
+ void setValueT2DArrayArray(RenderingContext gl, v, [AngleTextures? textures]) {
+ final n = v.length;
+
+ final units = allocTexUnits(textures, n);
+
+ if (!arraysEqual(cache, units)){
+ gl.uniform1iv(addr, units);
+ copyArray(cache, units);
+ }
+
+ for (int i = 0; i != n; ++i) {
+ textures?.setTexture2DArray(v[i] ?? emptyArrayTexture, units[i]);
+ }
+ }
+
+ // Helper to pick the right setter for a pure (bottom-level) array
+
+ dynamic getPureArraySetter(int type) {
+ switch (type) {
+ case 0x1406: return setValueV1fArray; // FLOAT
+ case 0x8b50: return setValueV2fArray; // _VEC2
+ case 0x8b51: return setValueV3fArray; // _VEC3
+ case 0x8b52: return setValueV4fArray; // _VEC4
+
+ case 0x8b5a: return setValueM2Array; // _MAT2
+ case 0x8b5b: return setValueM3Array; // _MAT3
+ case 0x8b5c: return setValueM4Array; // _MAT4
+
+ case 0x1404: case 0x8b56: return setValueV1iArray; // INT, BOOL
+ case 0x8b53: case 0x8b57: return setValueV2iArray; // _VEC2
+ case 0x8b54: case 0x8b58: return setValueV3iArray; // _VEC3
+ case 0x8b55: case 0x8b59: return setValueV4iArray; // _VEC4
+
+ case 0x1405: return setValueV1uiArray; // UINT
+ case 0x8dc6: return setValueV2uiArray; // _VEC2
+ case 0x8dc7: return setValueV3uiArray; // _VEC3
+ case 0x8dc8: return setValueV4uiArray; // _VEC4
+
+ case 0x8b5e: // SAMPLER_2D
+ case 0x8d66: // SAMPLER_EXTERNAL_OES
+ case 0x8dca: // INT_SAMPLER_2D
+ case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D
+ case 0x8b62: // SAMPLER_2D_SHADOW
+ return setValueT1Array;
+
+ case 0x8b5f: // SAMPLER_3D
+ case 0x8dcb: // INT_SAMPLER_3D
+ case 0x8dd3: // UNSIGNED_INT_SAMPLER_3D
+ return setValueT3DArray;
+
+ case 0x8b60: // SAMPLER_CUBE
+ case 0x8dcc: // INT_SAMPLER_CUBE
+ case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE
+ case 0x8dc5: // SAMPLER_CUBE_SHADOW
+ return setValueT6Array;
+
+ case 0x8dc1: // SAMPLER_2D_ARRAY
+ case 0x8dcf: // INT_SAMPLER_2D_ARRAY
+ case 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY
+ case 0x8dc4: // SAMPLER_2D_ARRAY_SHADOW
+ return setValueT2DArrayArray;
+ }
+ }
+}
diff --git a/packages/three_js_angle_renderer/lib/angle/angle_utils.dart b/packages/three_js_angle_renderer/lib/angle/angle_utils.dart
new file mode 100755
index 00000000..b2b180ef
--- /dev/null
+++ b/packages/three_js_angle_renderer/lib/angle/angle_utils.dart
@@ -0,0 +1,184 @@
+part of three_webgl;
+
+class AngleUtils {
+ bool _didDispose = false;
+ AngleExtensions extensions;
+
+ AngleUtils(this.extensions);
+
+ void dispose(){
+ if(_didDispose) return;
+ _didDispose = true;
+ extensions.dispose();
+ }
+
+ dynamic convert(int p, [String colorSpace = NoColorSpace]) {
+ dynamic extension;
+ final transfer = ColorManagement.getTransfer(ColorSpace.fromString(colorSpace));
+
+ if (p == UnsignedByteType) return WebGL.UNSIGNED_BYTE;
+ if (p == UnsignedShort4444Type) return WebGL.UNSIGNED_SHORT_4_4_4_4;
+ if (p == UnsignedShort5551Type) return WebGL.UNSIGNED_SHORT_5_5_5_1;
+ if ( p == UnsignedInt5999Type ) return WebGL.UNSIGNED_INT_5_9_9_9_REV;
+
+ if (p == ByteType) return WebGL.BYTE;
+ if (p == ShortType) return WebGL.SHORT;
+ if (p == UnsignedShortType) return WebGL.UNSIGNED_SHORT;
+ if (p == IntType) return WebGL.INT;
+ if (p == UnsignedIntType) return WebGL.UNSIGNED_INT;
+ if (p == FloatType) return WebGL.FLOAT;
+ if (p == HalfFloatType) return WebGL.HALF_FLOAT;
+
+ if (p == AlphaFormat) return WebGL.ALPHA;
+ if (p == RGBFormat ) return WebGL.RGB;
+ if (p == RGBAFormat) return WebGL.RGBA;
+ if (p == LuminanceFormat) return WebGL.LUMINANCE;
+ if (p == LuminanceAlphaFormat) return WebGL.LUMINANCE_ALPHA;
+ if (p == DepthFormat) return WebGL.DEPTH_COMPONENT;
+ if (p == DepthStencilFormat) return WebGL.DEPTH_STENCIL;
+
+ if (p == RedFormat) return WebGL.RED;
+ if (p == RedIntegerFormat ) return WebGL.RED_INTEGER;
+ if (p == RGFormat ) return WebGL.RG;
+ if (p == RGIntegerFormat ) return WebGL.RG_INTEGER;
+ if (p == RGBAIntegerFormat ) return WebGL.RGBA_INTEGER;
+
+ // S3TC
+
+ if (p == RGB_S3TC_DXT1_Format ||
+ p == RGBA_S3TC_DXT1_Format ||
+ p == RGBA_S3TC_DXT3_Format ||
+ p == RGBA_S3TC_DXT5_Format
+ ) {
+ if (transfer == SRGBTransfer) {
+ extension = extensions.get('WEBGL_compressed_texture_s3tc_srgb');
+
+ if (extension != null) {
+ if ( p == RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_SRGB_S3TC_DXT1_EXT;
+ if ( p == RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT;
+ if ( p == RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT;
+ if ( p == RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT;
+ } else {
+ return null;
+ }
+ }
+ else {
+ extension = extensions.get('WEBGL_compressed_texture_s3tc');
+
+ if (extension != null) {
+ if ( p == RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT;
+ if ( p == RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT;
+ if ( p == RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT;
+ if ( p == RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT;
+ } else {
+ return null;
+ }
+ }
+ }
+
+ // PVRTC
+
+ if (p == RGB_PVRTC_4BPPV1_Format ||
+ p == RGB_PVRTC_2BPPV1_Format ||
+ p == RGBA_PVRTC_4BPPV1_Format ||
+ p == RGBA_PVRTC_2BPPV1_Format) {
+ extension = extensions.get('WEBGL_compressed_texture_pvrtc');
+
+ if (extension != null) {
+ if ( p == RGB_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG;
+ if ( p == RGB_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
+ if ( p == RGBA_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
+ if ( p == RGBA_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
+ } else {
+ return null;
+ }
+ }
+
+ // ETC1
+
+ if (p == RGB_ETC1_Format || p == RGB_ETC2_Format || p == RGBA_ETC2_EAC_Format) {
+ extension = extensions.get('WEBGL_compressed_texture_etc1');
+ if ( extension != null ) {
+ if ( p == RGB_ETC1_Format || p == RGB_ETC2_Format ) return ( transfer == SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ETC2 : extension.COMPRESSED_RGB8_ETC2;
+ if ( p == RGBA_ETC2_EAC_Format ) return ( transfer == SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC : extension.COMPRESSED_RGBA8_ETC2_EAC;
+ } else {
+ return null;
+ }
+ }
+
+ // ASTC
+
+ if (p == RGBA_ASTC_4x4_Format ||
+ p == RGBA_ASTC_5x4_Format ||
+ p == RGBA_ASTC_5x5_Format ||
+ p == RGBA_ASTC_6x5_Format ||
+ p == RGBA_ASTC_6x6_Format ||
+ p == RGBA_ASTC_8x5_Format ||
+ p == RGBA_ASTC_8x6_Format ||
+ p == RGBA_ASTC_8x8_Format ||
+ p == RGBA_ASTC_10x5_Format ||
+ p == RGBA_ASTC_10x6_Format ||
+ p == RGBA_ASTC_10x8_Format ||
+ p == RGBA_ASTC_10x10_Format ||
+ p == RGBA_ASTC_12x10_Format ||
+ p == RGBA_ASTC_12x12_Format) {
+ extension = extensions.get('WEBGL_compressed_texture_astc');
+
+ if (extension != null) {
+ if ( p == RGBA_ASTC_4x4_Format ) return ( transfer == SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR : extension.COMPRESSED_RGBA_ASTC_4x4_KHR;
+ if ( p == RGBA_ASTC_5x4_Format ) return ( transfer == SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR : extension.COMPRESSED_RGBA_ASTC_5x4_KHR;
+ if ( p == RGBA_ASTC_5x5_Format ) return ( transfer == SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR : extension.COMPRESSED_RGBA_ASTC_5x5_KHR;
+ if ( p == RGBA_ASTC_6x5_Format ) return ( transfer == SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR : extension.COMPRESSED_RGBA_ASTC_6x5_KHR;
+ if ( p == RGBA_ASTC_6x6_Format ) return ( transfer == SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR : extension.COMPRESSED_RGBA_ASTC_6x6_KHR;
+ if ( p == RGBA_ASTC_8x5_Format ) return ( transfer == SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR : extension.COMPRESSED_RGBA_ASTC_8x5_KHR;
+ if ( p == RGBA_ASTC_8x6_Format ) return ( transfer == SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR : extension.COMPRESSED_RGBA_ASTC_8x6_KHR;
+ if ( p == RGBA_ASTC_8x8_Format ) return ( transfer == SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR : extension.COMPRESSED_RGBA_ASTC_8x8_KHR;
+ if ( p == RGBA_ASTC_10x5_Format ) return ( transfer == SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR : extension.COMPRESSED_RGBA_ASTC_10x5_KHR;
+ if ( p == RGBA_ASTC_10x6_Format ) return ( transfer == SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR : extension.COMPRESSED_RGBA_ASTC_10x6_KHR;
+ if ( p == RGBA_ASTC_10x8_Format ) return ( transfer == SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR : extension.COMPRESSED_RGBA_ASTC_10x8_KHR;
+ if ( p == RGBA_ASTC_10x10_Format ) return ( transfer == SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR : extension.COMPRESSED_RGBA_ASTC_10x10_KHR;
+ if ( p == RGBA_ASTC_12x10_Format ) return ( transfer == SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR : extension.COMPRESSED_RGBA_ASTC_12x10_KHR;
+ if ( p == RGBA_ASTC_12x12_Format ) return ( transfer == SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR : extension.COMPRESSED_RGBA_ASTC_12x12_KHR;
+ } else {
+ return null;
+ }
+ }
+
+ // BPTC
+
+ if ( p == RGBA_BPTC_Format || p == RGB_BPTC_SIGNED_Format || p == RGB_BPTC_UNSIGNED_Format ) {
+ extension = extensions.get( 'EXT_texture_compression_bptc' );
+
+ if ( extension != null ) {
+ if ( p == RGBA_BPTC_Format ) return ( transfer == SRGBTransfer ) ? extension.COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT : extension.COMPRESSED_RGBA_BPTC_UNORM_EXT;
+ if ( p == RGB_BPTC_SIGNED_Format ) return extension.COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT;
+ if ( p == RGB_BPTC_UNSIGNED_Format ) return extension.COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT;
+ } else {
+ return null;
+ }
+ }
+
+ // RGTC
+
+ if ( p == RED_RGTC1_Format || p == SIGNED_RED_RGTC1_Format || p == RED_GREEN_RGTC2_Format || p == SIGNED_RED_GREEN_RGTC2_Format ) {
+ extension = extensions.get( 'EXT_texture_compression_rgtc' );
+
+ if ( extension != null ) {
+ if ( p == RGBA_BPTC_Format ) return extension.COMPRESSED_RED_RGTC1_EXT;
+ if ( p == SIGNED_RED_RGTC1_Format ) return extension.COMPRESSED_SIGNED_RED_RGTC1_EXT;
+ if ( p == RED_GREEN_RGTC2_Format ) return extension.COMPRESSED_RED_GREEN_RGTC2_EXT;
+ if ( p == SIGNED_RED_GREEN_RGTC2_Format ) return extension.COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT;
+ } else {
+ return null;
+ }
+ }
+
+ //
+
+ if ( p == UnsignedInt248Type ) return WebGL.UNSIGNED_INT_24_8;
+
+ // if "p" can't be resolved, assume the user defines a WebGL constant as a string (fallback/workaround for packed RGB formats)
+
+ return (WebGL.get(p) != null) ? WebGL.get(p) : null;
+ }
+}
diff --git a/packages/three_js_angle_renderer/lib/angle/index.dart b/packages/three_js_angle_renderer/lib/angle/index.dart
new file mode 100755
index 00000000..09d9207e
--- /dev/null
+++ b/packages/three_js_angle_renderer/lib/angle/index.dart
@@ -0,0 +1,47 @@
+library three_webgl;
+
+import 'dart:typed_data';
+import 'dart:math' as math;
+
+import 'package:flutter/foundation.dart';
+import 'package:three_js_core/three_js_core.dart';
+import 'package:three_js_math/three_js_math.dart';
+import '../renderers/index.dart';
+import '../shaders/index.dart';
+import '../renderers/pmrem_generator.dart';
+import 'package:flutter_angle/flutter_angle.dart';
+
+part 'angle_animation.dart';
+part 'angle_attributes.dart';
+part 'angle_background.dart';
+part 'angle_binding_states.dart';
+part 'angle_buffer_renderer.dart';
+part 'angle_capabilities.dart';
+part 'angle_clipping.dart';
+part 'angle_cube_maps.dart';
+part 'angle_extensions.dart';
+part 'angle_geometries.dart';
+part 'angle_indexed_buffer_renderer.dart';
+part 'angle_info.dart';
+part 'angle_lights.dart';
+part 'angle_materials.dart';
+part 'angle_morphtargets.dart';
+part 'angle_objects.dart';
+part 'angle_program.dart';
+part 'angle_program_extra.dart';
+part 'angle_programs.dart';
+part 'angle_properties.dart';
+part 'angle_render_list.dart';
+part 'angle_render_lists.dart';
+
+part 'angle_render_states.dart';
+part 'angle_shader.dart';
+part 'angle_shadow_map.dart';
+part 'angle_state.dart';
+part 'angle_textures.dart';
+part 'angle_uniforms.dart';
+part 'angle_uniforms_groups.dart';
+part 'angle_uniforms_helper.dart';
+part 'angle_utils.dart';
+part 'angle_cube_uv_maps.dart';
+part 'angle_shader_cache.dart';
diff --git a/packages/three_js_angle_renderer/lib/renderers/3d_render_target.dart b/packages/three_js_angle_renderer/lib/renderers/3d_render_target.dart
new file mode 100755
index 00000000..4a96a6ce
--- /dev/null
+++ b/packages/three_js_angle_renderer/lib/renderers/3d_render_target.dart
@@ -0,0 +1,9 @@
+part of three_renderers;
+
+class Angle3DRenderTarget extends RenderTarget {
+ Angle3DRenderTarget([int width = 1, int height = 1, int depth = 1, RenderTargetOptions? options]) : super(width, height, options) {
+ this.depth = depth;
+ texture = Data3DTexture(null, width, height, depth);
+ texture.isRenderTargetTexture = true;
+ }
+}
diff --git a/packages/three_js_angle_renderer/lib/renderers/array_render_target.dart b/packages/three_js_angle_renderer/lib/renderers/array_render_target.dart
new file mode 100755
index 00000000..0d9553af
--- /dev/null
+++ b/packages/three_js_angle_renderer/lib/renderers/array_render_target.dart
@@ -0,0 +1,9 @@
+part of three_renderers;
+
+class AngleArrayRenderTarget extends RenderTarget {
+ AngleArrayRenderTarget([int width = 1, int height = 1, int depth = 1, RenderTargetOptions? options]) : super(width, height, options) {
+ this.depth = depth;
+ texture = DataArrayTexture(null, width, height, depth);
+ texture.isRenderTargetTexture = true;
+ }
+}
\ No newline at end of file
diff --git a/packages/three_js_angle_renderer/lib/renderers/cube_render_target.dart b/packages/three_js_angle_renderer/lib/renderers/cube_render_target.dart
new file mode 100755
index 00000000..b129bc83
--- /dev/null
+++ b/packages/three_js_angle_renderer/lib/renderers/cube_render_target.dart
@@ -0,0 +1,126 @@
+// part of three_renderers;
+
+// class AngleCubeRenderTarget extends CubeRenderTarget {
+// AngleCubeRenderTarget([int size = 1, RenderTargetOptions? options]) : super(size, options) {
+// // By convention -- likely based on the RenderMan spec from the 1990's -- cube maps are specified by WebGL (and three.js)
+// // in a coordinate system in which positive-x is to the right when looking up the positive-z axis -- in other words,
+// // in a left-handed coordinate system. By continuing this convention, preexisting cube maps continued to render correctly.
+
+// // three.js uses a right-handed coordinate system. So environment maps used in three.js appear to have px and nx swapped
+// // and the flag isRenderTargetTexture controls this conversion. The flip is not required when using WebGLCubeRenderTarget.texture
+// // as a cube texture (this is detected when isRenderTargetTexture is set to true for cube textures).
+// final image = ImageElement(width: size, height: size, depth: 1);
+// final images = [image, image, image, image, image, image];
+
+// texture = CubeTexture(
+// images,
+// this.options.mapping,
+// this.options.wrapS,
+// this.options.wrapT,
+// this.options.magFilter,
+// this.options.minFilter,
+// this.options.format,
+// this.options.type,
+// this.options.anisotropy,
+// this.options.colorSpace
+// );
+// texture.isRenderTargetTexture = true;
+
+// texture.generateMipmaps = this.options.generateMipmaps;
+// texture.minFilter = this.options.minFilter ?? LinearFilter;
+// }
+
+// @override
+// AngleCubeRenderTarget fromEquirectangularTexture(Renderer renderer, Texture texture) {
+// this.texture.type = texture.type;
+// this.texture.colorSpace = texture.colorSpace;
+
+// this.texture.generateMipmaps = texture.generateMipmaps;
+// this.texture.minFilter = texture.minFilter;
+// this.texture.magFilter = texture.magFilter;
+
+// final shader = {
+// "uniforms": {
+// "tEquirect": {'value': null},
+// },
+// "vertexShader": """
+
+// varying vec3 vWorldDirection;
+
+// vec3 transformDirection( in vec3 dir, in mat4 matrix ) {
+
+// return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );
+
+// }
+
+// void main() {
+
+// vWorldDirection = transformDirection( position, modelMatrix );
+
+// #include
+// #include
+
+// }
+// """,
+// "fragmentShader": """
+
+// uniform sampler2D tEquirect;
+
+// varying vec3 vWorldDirection;
+
+// #include
+
+// void main() {
+
+// vec3 direction = normalize( vWorldDirection );
+
+// vec2 sampleUV = equirectUv( direction );
+
+// gl_FragColor = texture2D( tEquirect, sampleUV );
+
+// }
+// """
+// };
+
+// final geometry = BoxGeometry(5, 5, 5);
+// final material = ShaderMaterial.fromMap({
+// "name": 'CubemapFromEquirect',
+// "uniforms": cloneUniforms(shader["uniforms"] as Map),
+// "vertexShader": shader["vertexShader"],
+// "fragmentShader": shader["fragmentShader"],
+// "side": BackSide,
+// "blending": NoBlending
+// });
+
+// material.uniforms["tEquirect"]["value"] = texture;
+
+// final mesh = Mesh(geometry, material);
+
+// final currentMinFilter = texture.minFilter;
+
+// // Avoid blurred poles
+// if (texture.minFilter == LinearMipmapLinearFilter) {
+// texture.minFilter = LinearFilter;
+// }
+
+// final camera = CubeCamera(1, 10, this);
+// camera.update(renderer, mesh);
+
+// texture.minFilter = currentMinFilter;
+
+// mesh.geometry!.dispose();
+// mesh.material?.dispose();
+
+// return this;
+// }
+
+// @override
+// void clear(Renderer renderer, [bool color = true, bool depth = true, bool stencil = true]) {
+// final currentRenderTarget = renderer.getRenderTarget();
+// for (int i = 0; i < 6; i++) {
+// renderer.setRenderTarget(this, i);
+// renderer.clear(color, depth, stencil);
+// }
+// renderer.setRenderTarget(currentRenderTarget);
+// }
+// }
\ No newline at end of file
diff --git a/packages/three_js_angle_renderer/lib/renderers/index.dart b/packages/three_js_angle_renderer/lib/renderers/index.dart
new file mode 100755
index 00000000..92e79a3b
--- /dev/null
+++ b/packages/three_js_angle_renderer/lib/renderers/index.dart
@@ -0,0 +1,17 @@
+library three_renderers;
+
+import 'dart:math' as math;
+import 'dart:typed_data';
+import '../shaders/index.dart';
+
+import 'package:three_js_core/three_js_core.dart';
+import 'package:flutter/foundation.dart';
+import 'package:flutter_angle/flutter_angle.dart';
+import 'package:three_js_math/three_js_math.dart';
+import '../angle/index.dart';
+
+part 'renderer.dart';
+//part 'render_target.dart';
+part '3d_render_target.dart';
+part 'array_render_target.dart';
+//part 'cube_render_target.dart';
diff --git a/packages/three_js_core/lib/renderers/pmrem_generator.dart b/packages/three_js_angle_renderer/lib/renderers/pmrem_generator.dart
similarity index 97%
rename from packages/three_js_core/lib/renderers/pmrem_generator.dart
rename to packages/three_js_angle_renderer/lib/renderers/pmrem_generator.dart
index e8a0e7a1..aa49c6f3 100755
--- a/packages/three_js_core/lib/renderers/pmrem_generator.dart
+++ b/packages/three_js_angle_renderer/lib/renderers/pmrem_generator.dart
@@ -1,6 +1,7 @@
import 'package:flutter/foundation.dart';
import 'package:three_js_core/three_js_core.dart';
import 'package:three_js_math/three_js_math.dart';
+import './index.dart';
import 'dart:math' as math;
final _origin = Vector3();
@@ -56,7 +57,7 @@ class PMREMGenerator {
late final double invPhi;
late final List _axisDirections;
- late WebGLRenderer _renderer;
+ late AngleRenderer _renderer;
dynamic _pingPongRenderTarget;
dynamic _blurMaterial;
dynamic _equirectMaterial;
@@ -65,7 +66,7 @@ class PMREMGenerator {
late int _lodMax;
late int _cubeSize;
- PMREMGenerator(WebGLRenderer renderer) {
+ PMREMGenerator(AngleRenderer renderer) {
// Golden Ratio
phi = (1 + math.sqrt(5)) / 2;
invPhi = 1 / phi;
@@ -105,7 +106,7 @@ class PMREMGenerator {
/// * and far planes ensure the scene is rendered in its entirety (the cubeCamera
/// * is placed at the origin).
/// *
- WebGLRenderTarget fromScene(Scene scene, {double sigma = 0, double near = 0.1, double far = 100, PMREMGeneratorOptions? options}) {
+ RenderTarget fromScene(Scene scene, {double sigma = 0, double near = 0.1, double far = 100, PMREMGeneratorOptions? options}) {
options ??= PMREMGeneratorOptions();
_oldTarget = _renderer.getRenderTarget();
@@ -236,7 +237,7 @@ class PMREMGenerator {
return cubeUVRenderTarget;
}
- WebGLRenderTarget _allocateTargets() {
+ RenderTarget _allocateTargets() {
int width = 3 * math.max(_cubeSize, 16 * 7);
int height = 4 * _cubeSize;
@@ -439,13 +440,13 @@ class PMREMGenerator {
console.warning("sigmaRadians, $sigmaRadians, is too large and will clip, as it requested $samples samples when the maximum is set to $maxSamples");
}
- List weights = [];
+ Float32List weights = Float32List(maxSamples);
double sum = 0;
for (int i = 0; i < maxSamples; ++i) {
final x = i / sigmaPixels;
final weight = math.exp(-x * x / 2);
- weights.add(weight);
+ weights[i] = weight;
if (i == 0) {
sum += weight;
@@ -460,7 +461,7 @@ class PMREMGenerator {
blurUniforms['envMap']["value"] = targetIn?.texture;
blurUniforms['samples']["value"] = samples;
- blurUniforms['weights']["value"] = Float32List.fromList(weights);
+ blurUniforms['weights']["value"] = weights;
blurUniforms['latitudinal']["value"] = direction == 'latitudinal';
if (poleAxis != null) {
@@ -551,8 +552,8 @@ class PMREMGenerator {
return {"lodPlanes": lodPlanes, "sizeLods": sizeLods, "sigmas": sigmas};
}
- WebGLRenderTarget _createRenderTarget(int width, int height, Map params) {
- final cubeUVRenderTarget = WebGLRenderTarget(width, height, WebGLRenderTargetOptions(params));
+ RenderTarget _createRenderTarget(int width, int height, Map params) {
+ final cubeUVRenderTarget = RenderTarget(width, height, RenderTargetOptions(params));
cubeUVRenderTarget.texture.mapping = CubeUVReflectionMapping;
cubeUVRenderTarget.texture.name = 'PMREM.cubeUv';
cubeUVRenderTarget.scissorTest = true;
diff --git a/packages/three_js_angle_renderer/lib/renderers/render_target.dart b/packages/three_js_angle_renderer/lib/renderers/render_target.dart
new file mode 100755
index 00000000..564e2cc3
--- /dev/null
+++ b/packages/three_js_angle_renderer/lib/renderers/render_target.dart
@@ -0,0 +1,44 @@
+// /*
+// In options, we can specify:
+// * Texture parameters for an auto-generated target texture
+// * depthBuffer/stencilBuffer: Booleans to indicate if we should generate these buffers
+// */
+
+// part of three_renderers;
+
+// class AngleRenderTarget extends RenderTarget {
+// bool _didDispose = false;
+
+// AngleRenderTarget(super.width, super.height, [super.options]);
+
+// @override
+// AngleRenderTarget clone() {
+// return AngleRenderTarget(width, height, options)..copy(this);
+// }
+
+// @override
+// AngleRenderTarget copy(RenderTarget source) {
+// super.copy(source);
+// return this;
+// }
+
+// @override
+// bool is3D() {
+// return texture is Data3DTexture || texture is DataArrayTexture;
+// }
+
+// @override
+// void dispose() {
+// if(_didDispose) return;
+// _didDispose = true;
+// dispatchEvent(Event(type: "dispose"));
+// depthTexture?.dispose();
+// texture.dispose();
+// options.dispose();
+
+// // textures.forEach((t){
+// // t.dispose();
+// // });
+// // textures.clear();
+// }
+// }
\ No newline at end of file
diff --git a/packages/three_js_angle_renderer/lib/renderers/renderer.dart b/packages/three_js_angle_renderer/lib/renderers/renderer.dart
new file mode 100755
index 00000000..5267183d
--- /dev/null
+++ b/packages/three_js_angle_renderer/lib/renderers/renderer.dart
@@ -0,0 +1,2150 @@
+part of three_renderers;
+
+class AngleRendererParameters{
+ double width;
+ double height;
+ RenderingContext gl;
+ bool stencil;
+ bool antialias;
+ bool alpha;
+ int clearColor;
+ double clearAlpha;
+ bool logarithmicDepthBuffer;
+ bool premultipliedAlpha;
+ bool preserveDrawingBuffer;
+ PowerPreference powerPreference;
+ bool reverseDepthBuffer;
+ bool failIfMajorPerformanceCaveat;
+ bool depth = true;
+ Precision precision;
+ XRManager Function(AngleRenderer renderer, dynamic gl)? xr;
+
+ AngleRendererParameters({
+ required this.width,
+ required this.height,
+ required this.gl,
+ this.stencil = true,
+ this.antialias = false,
+ this.alpha = false,
+ this.clearAlpha = 1.0,
+ this.clearColor = 0x000000,
+ this.logarithmicDepthBuffer = false,
+ this.depth = true,
+ this.premultipliedAlpha = true,
+ this.preserveDrawingBuffer = false,
+ this.powerPreference = PowerPreference.defaultp,
+ this.failIfMajorPerformanceCaveat = false,
+ this.reverseDepthBuffer = false,
+ this.precision = Precision.highp,
+ this.xr,
+ });
+
+ factory AngleRendererParameters.fromMap(Map map){
+ return AngleRendererParameters(
+ width: map["width"].toDouble(),
+ height: map["height"].toDouble(),
+ depth: map["depth"] ?? true,
+ stencil: map["stencil"] ?? true,
+ antialias: map["antialias"] ?? false,
+ premultipliedAlpha: map["premultipliedAlpha"] ?? true,
+ preserveDrawingBuffer: map["preserveDrawingBuffer"] ?? false,
+ powerPreference: map["powerPreference"] ?? "default",
+ failIfMajorPerformanceCaveat: map["failIfMajorPerformanceCaveat"] ?? false,
+ alpha: map["alpha"] ?? false,
+ gl: map["gl"],
+ xr: map["xr"],
+ precision: map['precision'],
+ reverseDepthBuffer: map['reverseDepthBuffer']
+ );
+ }
+}
+
+class AngleRenderer extends Renderer{
+ late AngleRendererParameters parameters;
+
+ bool _didDispose = false;
+ bool alpha = false;
+ bool depth = true;
+ bool stencil = true;
+ bool antialias = false;
+
+ final currentClearColor = Color.fromHex32( 0x000000 );
+ double currentClearAlpha = 0;
+
+ bool premultipliedAlpha = true;
+ bool preserveDrawingBuffer = false;
+ PowerPreference powerPreference = PowerPreference.defaultp;
+ bool failIfMajorPerformanceCaveat = false;
+ bool reverseDepthBuffer = false;
+
+ bool renderBackground = false;
+ AngleRenderList? currentRenderList;
+ AngleRenderState? currentRenderState;
+
+ // render() can be called from within a callback triggered by another render.
+
+ // We track this so that the nested render call gets its list and state isolated from the parent render call.
+
+ List renderListStack = [];
+ List renderStateStack = [];
+
+ // Debug configuration container
+ Map debug = {
+ /// Enables error checking and reporting when shader programs are being compiled
+ /// @type {boolean}
+ "checkShaderErrors": true
+ };
+
+ // clearing
+ bool useLegacyLights = false;
+
+ // user-defined clipping
+ List clippingPlanes = [];
+ bool localClippingEnabled = false;
+
+ // physically based shading
+ int outputEncoding = LinearEncoding;
+
+ // physical lights
+ bool physicallyCorrectLights = false;
+
+ // tone mapping
+ late double _width;
+ late double _height;
+
+ double get width => _width;
+ double get height => _height;
+
+ late Vector4 _viewport;
+ late Vector4 _scissor;
+
+ // internal properties
+
+ bool _isContextLost = false;
+
+ // internal state cache
+
+ int _currentActiveCubeFace = 0;
+ int _currentActiveMipmapLevel = 0;
+ RenderTarget? _currentRenderTarget;
+
+ int _currentMaterialId = -1;
+ Camera? _currentCamera;
+
+ final _currentProjectionMatrix = Matrix4();
+ final _currentViewport = Vector4.identity();
+ final _currentScissor = Vector4.identity();
+ bool? _currentScissorTest;
+
+ double _pixelRatio = 1;
+ Function? _opaqueSort;
+ Function? _transparentSort;
+
+ bool _scissorTest = false;
+
+ // frustum
+
+ final _frustum = Frustum();
+
+ // clipping
+
+ bool _clippingEnabled = false;
+ bool _localClippingEnabled = false;
+
+ // transmission
+ double transmissionResolutionScale = 1.0;
+
+ // camera matrices cache
+
+ final projScreenMatrix = Matrix4.identity();
+
+ final _vector3 = Vector3();
+ final _vector4 = Vector4();
+
+ final _emptyScene = Scene();
+
+ @override
+ double getTargetPixelRatio() => _currentRenderTarget == null ? _pixelRatio : 1.0;
+
+ // initialize
+
+ late RenderingContext _gl;
+
+ RenderingContext get gl => _gl;
+
+ final animation = AngleAnimation();
+ late AngleExtensions extensions;
+ late Capabilities capabilities;
+ late AngleInfo info;
+ late AngleProperties properties;
+ late AngleTextures textures;
+ late AngleCubeMaps cubemaps;
+ late AngleCubeUVMaps cubeuvmaps;
+ late AngleAttributes attributes;
+ late AngleGeometries geometries;
+ late AngleObjects objects;
+ late AnglePrograms programCache;
+ late AngleMaterials materials;
+ late AngleRenderLists renderLists;
+ late AngleRenderStates renderStates;
+ late AngleClipping clipping;
+
+ late AngleBackground background;
+ late AngleMorphtargets morphtargets;
+ late BaseAngleBufferRenderer bufferRenderer;
+ late AngleIndexedBufferRenderer indexedBufferRenderer;
+
+ late AngleUtils utils;
+
+ late AngleBindingStates bindingStates;
+
+ late XRManager Function(AngleRenderer renderer, dynamic gl)? _setXR;
+ late AngleUniformsGroups uniformsGroups;
+
+ late final Framebuffer _scratchFrameBuffer;
+ late final Framebuffer _srcFramebuffer;
+ late final Framebuffer _dstFramebuffer;
+ AngleRenderer(this.parameters) {
+ shaderChunk = angleShaderChunk;
+ lightsFragmentBegin = angleLightsFragmentBegin;
+ lightsParsBegin = angleLightsParsBegin;
+
+ _width = this.parameters.width;
+ _height = this.parameters.height;
+
+ depth = this.parameters.depth;
+ stencil = this.parameters.stencil;
+ antialias = this.parameters.antialias;
+ premultipliedAlpha = this.parameters.premultipliedAlpha;
+ preserveDrawingBuffer = this.parameters.preserveDrawingBuffer;
+ powerPreference = this.parameters.powerPreference;
+
+ failIfMajorPerformanceCaveat = this.parameters.failIfMajorPerformanceCaveat;
+
+ alpha = this.parameters.alpha;
+
+ _viewport = Vector4(0, 0, width, height);
+ _scissor = Vector4(0, 0, width, height);
+
+ _gl = this.parameters.gl;
+ _setXR = this.parameters.xr;
+
+ initGLContext();
+ }
+ factory AngleRenderer.fromMap([Map? parameters]) {
+ return AngleRenderer(AngleRendererParameters.fromMap(parameters ?? {}));
+ }
+
+ void initGLContext() {
+ _scratchFrameBuffer = _gl.createFramebuffer();
+ _srcFramebuffer = _gl.createFramebuffer();
+ _dstFramebuffer = _gl.createFramebuffer();
+
+ extensions = AngleExtensions(_gl);
+ extensions.init();
+ utils = AngleUtils(extensions);
+
+ capabilities = AngleCapabilities(_gl, extensions, parameters, utils);
+ state = AngleState(_gl,extensions);
+
+ if ( (capabilities as AngleCapabilities).reverseDepthBuffer && reverseDepthBuffer ) {
+ state.buffers['depth'].setReversed( true );
+ }
+
+ info = AngleInfo(_gl);
+ properties = AngleProperties();
+ textures = AngleTextures(_gl, extensions, state as AngleState, properties, capabilities as AngleCapabilities, utils, info);
+ cubemaps = AngleCubeMaps(this);
+ cubeuvmaps = AngleCubeUVMaps(this);
+ attributes = AngleAttributes(_gl);
+ bindingStates = AngleBindingStates(_gl, attributes);
+ geometries = AngleGeometries(_gl, attributes, info, bindingStates);
+ objects = AngleObjects(_gl, geometries, attributes, info);
+ morphtargets = AngleMorphtargets(_gl, capabilities as AngleCapabilities, textures);
+ clipping = AngleClipping(properties);
+ programCache = AnglePrograms(this, cubemaps, cubeuvmaps, extensions, capabilities as AngleCapabilities, bindingStates, clipping);
+ materials = AngleMaterials(this, properties);
+ renderLists = AngleRenderLists();
+ renderStates = AngleRenderStates(extensions);
+ background = AngleBackground(this, cubemaps, cubeuvmaps, state as AngleState, objects, alpha, premultipliedAlpha);
+ shadowMap = AngleShadowMap(this, objects, capabilities as AngleCapabilities);
+ uniformsGroups = AngleUniformsGroups( _gl, info, capabilities as AngleCapabilities, state as AngleState );
+
+ bufferRenderer = AngleBufferRenderer(_gl, extensions, info);
+ indexedBufferRenderer = AngleIndexedBufferRenderer(_gl, extensions, info);
+
+ info.programs = programCache.programs;
+
+ xr = _setXR?.call(this,gl) ?? XRManager(this, _gl);
+ xr.init();
+ xr.addEventListener( 'sessionstart', onXRSessionStart );
+ xr.addEventListener( 'sessionend', onXRSessionEnd );
+ }
+
+ // API
+ @override
+ RenderingContext getContext() {
+ return _gl;
+ }
+
+ dynamic getContextAttributes() {
+ return _gl.getContextAttributes();
+ }
+
+ void forceContextLoss() {
+ final extension = extensions.get('WEBGL_lose_context');
+ if (extension) extension.loseContext();
+ }
+
+ void forceContextRestore() {
+ final extension = extensions.get('WEBGL_lose_context');
+ if (extension) extension.restoreContext();
+ }
+
+ @override
+ double getPixelRatio() {
+ return _pixelRatio;
+ }
+
+ void setPixelRatio(double value) {
+ _pixelRatio = value;
+ setSize(width, height, false);
+ }
+
+ @override
+ Vector2 getSize(Vector2 target) {
+ return target.setValues(width.toDouble(), height.toDouble());
+ }
+
+ void setSize(double width, double height, [bool updateStyle = false]) {
+ if ( xr.isPresenting ) {
+ console.warning( 'WebGLRenderer: Can\'t change size while VR device is presenting.' );
+ return;
+ }
+
+ _width = width;
+ _height = height;
+ setViewport(0, 0, width, height);
+ }
+
+ Vector2 getDrawingBufferSize(Vector2 target) {
+ return target.setValues(width * _pixelRatio, height * _pixelRatio).floor();
+ }
+
+ void setDrawingBufferSize(double width, double height, double pixelRatio) {
+ _width = width;
+ _height = height;
+ console.info("WebGLRenderer setDrawingBufferSize ");
+ setViewport(0, 0, width, height);
+ }
+
+ @override
+ Vector4 getCurrentViewport(Vector4 target) {
+ return target.setFrom(_currentViewport);
+ }
+
+ @override
+ Vector4 getViewport(Vector4 target) {
+ return target.setFrom(_viewport);
+ }
+
+ @override
+ void setViewport(double x, double y, double width, double height) {
+ _viewport.setValues(x, y, width, height);
+ _currentViewport.setFrom(_viewport);
+ _currentViewport.scale(_pixelRatio);
+ _currentViewport.floor();
+ state.viewport(_currentViewport);
+ }
+
+ Vector4 getScissor(Vector4 target) {
+ return target.setFrom(_scissor);
+ }
+
+ void setScissor(double x, double y, double width, double height) {
+ _scissor.setValues(x, y, width, height);
+ _currentScissor.setFrom(_scissor);
+ _currentScissor.scale(_pixelRatio);
+ _currentScissor.floor();
+ state.scissor(_currentScissor);
+ }
+
+ bool getScissorTest() {
+ return _scissorTest;
+ }
+
+ void setScissorTest(bool boolean) {
+ (state as AngleState).setScissorTest(_scissorTest = boolean);
+ }
+
+ void setOpaqueSort(Function? method) {
+ _opaqueSort = method;
+ }
+
+ void setTransparentSort(Function? method) {
+ _transparentSort = method;
+ }
+
+ // Clearing
+ @override
+ Color getClearColor(Color target) {
+ target.setFrom(background.getClearColor());
+ return target;
+ }
+
+ // color same as Color.set
+ @override
+ void setClearColor(Color color, [double alpha = 1.0]) {
+ background.setClearColor(color, alpha);
+ }
+
+ @override
+ double getClearAlpha() {
+ return background.getClearAlpha();
+ }
+
+ @override
+ void setClearAlpha(double alpha) {
+ background.setClearAlpha(alpha);
+ }
+
+ final uintClearColor = Uint32List( 4 );
+ final intClearColor = Uint32List( 4 );
+
+ @override
+ void clear([bool color = true, bool depth = true, bool stencil = true]) {
+ int bits = 0;
+
+ if ( color ) {
+ // check if we're trying to clear an integer target
+ bool isIntegerFormat = false;
+ if ( _currentRenderTarget != null ) {
+ final targetFormat = _currentRenderTarget!.texture.format;
+ isIntegerFormat = targetFormat == RGBAIntegerFormat ||
+ targetFormat == RGIntegerFormat ||
+ targetFormat == RedIntegerFormat;
+ }
+
+ // use the appropriate clear functions to clear the target if it's a signed
+ // or unsigned integer target
+ if ( isIntegerFormat ) {
+ final targetType = _currentRenderTarget!.texture.type;
+ final isUnsignedType = targetType == UnsignedByteType ||
+ targetType == UnsignedIntType ||
+ targetType == UnsignedShortType ||
+ targetType == UnsignedInt248Type ||
+ targetType == UnsignedShort4444Type ||
+ targetType == UnsignedShort5551Type;
+
+ final clearColor = background.getClearColor();
+ final a = background.getClearAlpha();
+ final r = clearColor.red;
+ final g = clearColor.green;
+ final b = clearColor.blue;
+
+ if ( isUnsignedType ) {
+ uintClearColor[ 0 ] = (r*255).toInt();
+ uintClearColor[ 1 ] = (g*255).toInt();
+ uintClearColor[ 2 ] = (b*255).toInt();
+ uintClearColor[ 3 ] = (a*255).toInt();
+ _gl.clearBufferuiv( WebGL.COLOR, 0, uintClearColor.buffer.asByteData().getUint32(0) );
+ }
+ else {
+ intClearColor[ 0 ] = (r*255).toInt();
+ intClearColor[ 1 ] = (g*255).toInt();
+ intClearColor[ 2 ] = (b*255).toInt();
+ intClearColor[ 3 ] = (a*255).toInt();
+ _gl.clearBufferiv( WebGL.COLOR, 0, intClearColor.buffer.asByteData().getInt32(0) );
+ }
+
+ }
+ else {
+ bits |= WebGL.COLOR_BUFFER_BIT;
+ }
+ }
+
+ if ( depth ) bits |= WebGL.DEPTH_BUFFER_BIT;
+ if ( stencil ) {
+ bits |= WebGL.STENCIL_BUFFER_BIT;
+ state.buffers['stencil'].setMask( 0xffffffff );
+ }
+
+ _gl.clear( bits );
+ }
+
+ //
+ @override
+ void dispose() {
+ if(_didDispose) return;
+ _didDispose = true;
+ state.reset();
+ attributes.dispose();
+ renderLists.dispose();
+ renderStates.dispose();
+ cubemaps.dispose();
+ cubeuvmaps.dispose();
+ bindingStates.dispose();
+ programCache.dispose();
+
+ currentRenderList?.dispose();
+ for(final stack in renderListStack){
+ stack.dispose();
+ }
+ renderListStack.clear();
+ for(final stack in renderStateStack){
+ stack.dispose();
+ }
+
+ renderStateStack.clear();
+ debug.clear();
+
+ _currentCamera?.clear();
+ _frustum.dispose();
+ _emptyScene.dispose();
+
+ extensions.dispose();
+ capabilities.dispose();
+ clipping.dispose();
+ utils.dispose();
+
+ state.dispose();
+ materials.dispose();
+ objects.dispose();
+ morphtargets.dispose();
+ indexedBufferRenderer.dispose();
+ background.dispose();
+ textures.dispose();
+ geometries.dispose();
+ shadowMap.dispose();
+
+ properties.dispose();
+
+ _gl.deleteFramebuffer(_scratchFrameBuffer);
+ _gl.deleteFramebuffer(_srcFramebuffer);
+ _gl.deleteFramebuffer(_dstFramebuffer);
+ }
+
+ // Events
+ void onContextLost( event ) {
+ //event.preventDefault();
+ console.info( 'THREE.WebGLRenderer: Context Lost.' );
+ _isContextLost = true;
+ }
+
+ void onContextRestore() {
+ console.info('WebGLRenderer: Context Restored.');
+
+ _isContextLost = false;
+
+ final infoAutoReset = info.autoReset;
+ final shadowMapEnabled = shadowMap.enabled;
+ final shadowMapAutoUpdate = shadowMap.autoUpdate;
+ final shadowMapNeedsUpdate = shadowMap.needsUpdate;
+ final shadowMapType = shadowMap.type;
+
+ initGLContext();
+
+ info.autoReset = infoAutoReset;
+ shadowMap.enabled = shadowMapEnabled;
+ shadowMap.autoUpdate = shadowMapAutoUpdate;
+ shadowMap.needsUpdate = shadowMapNeedsUpdate;
+ shadowMap.type = shadowMapType;
+ }
+
+ void onContextCreationError( event ) {
+ console.error( 'THREE.WebGLRenderer: A WebGL context could not be created. Reason: ${event.statusMessage}');
+ }
+
+ void onMaterialDispose(Event event) {
+ final material = event.target;
+ material.removeEventListener('dispose', onMaterialDispose);
+ deallocateMaterial(material);
+ }
+
+ // Buffer deallocation
+
+ void deallocateMaterial(Material material) {
+ releaseMaterialProgramReferences(material);
+ properties.remove(material);
+ }
+
+ void releaseMaterialProgramReferences(Material material) {
+ final programs = properties.get(material)["programs"];
+
+ if (programs != null) {
+ programs.forEach((key, program) {
+ programCache.releaseProgram(program);
+ });
+
+ if (material is ShaderMaterial) {
+ programCache.releaseShaderCache(material);
+ }
+ }
+ }
+
+ @override
+ void renderBufferDirect(
+ Camera camera,
+ Object3D? scene,
+ BufferGeometry geometry,
+ Material material,
+ Object3D object,
+ Map? group,
+ ) {
+ // print("renderBufferDirect .............material: $material ");
+ // renderBufferDirect second parameter used to be fog (could be null)
+ scene ??= _emptyScene;
+ final frontFaceCW = (object is Mesh && object.matrixWorld.determinant() < 0);
+
+ AngleProgram program = setProgram(camera, scene, geometry, material, object);
+
+ (state as AngleState).setMaterial(material, frontFaceCW);
+
+ BufferAttribute? index = geometry.index;
+ int rangeFactor = 1;
+
+ if (material.wireframe) {
+ index = geometries.getWireframeAttribute(geometry);
+ if (index == null) return;
+ if(kIsWeb && !kIsWasm){
+ rangeFactor = 2;
+ }
+ }
+
+ if (geometry.morphAttributes["position"] != null || geometry.morphAttributes["normal"] != null) {
+ morphtargets.update(object, geometry, program);
+ }
+
+ final drawRange = geometry.drawRange;
+ BufferAttribute? position = geometry.attributes["position"];
+ int drawStart = (drawRange['start'] ?? 0) * rangeFactor;
+ int drawEnd = ( (drawRange['start'] ?? 0) + (drawRange['count'] ?? 0) ) * rangeFactor;
+
+ if ( group != null ) {
+ drawStart = math.max( drawStart, group['start'] * rangeFactor );
+ drawEnd = math.min( drawEnd, ( group['start'] + group['count'] ) * rangeFactor );
+ }
+
+ if ( index != null ) {
+ drawStart = math.max( drawStart, 0 );
+ drawEnd = math.min( drawEnd, index.count );
+ } else if (position != null) {
+ drawStart = math.max( drawStart, 0 );
+ drawEnd = math.min( drawEnd, position.count );
+ }
+
+ final drawCount = drawEnd - drawStart;
+ if ( drawCount < 0 || drawCount == double.maxFinite.toInt() ) return;
+
+ bindingStates.setup(object, material, program, geometry, index);
+
+ Map attribute;
+ BaseAngleBufferRenderer renderer = bufferRenderer;
+
+ if (index != null) {
+ attribute = attributes.get(index);
+ renderer = indexedBufferRenderer;
+ renderer.setIndex(attribute);
+ }
+
+ if (object is Mesh) {
+ if (material.wireframe) {
+ (state as AngleState).setLineWidth(material.wireframeLinewidth! * getTargetPixelRatio());
+ renderer.setMode(WebGL.LINES);
+ }
+ else {
+ renderer.setMode(WebGL.TRIANGLES);
+ }
+ }
+ else if (object is Line) {
+ double? lineWidth = material.linewidth;
+
+ lineWidth ??= 1; // Not using Line*Material
+
+ (state as AngleState).setLineWidth(lineWidth * getTargetPixelRatio());
+
+ if (object is LineSegments) {
+ renderer.setMode(WebGL.LINES);
+ }
+ else if (object is LineLoop) {
+ renderer.setMode(WebGL.LINE_LOOP);
+ }
+ else {
+ renderer.setMode(WebGL.LINE_STRIP);
+ }
+ }
+ else if (object is Points) {
+ renderer.setMode(WebGL.POINTS);
+ }
+ else if (object is Sprite) {
+ renderer.setMode(WebGL.TRIANGLES);
+ }
+
+ if ( object is BatchedMesh ) {
+ if (object.multiDrawInstances != null ) {
+ renderer.renderMultiDrawInstances( object.multiDrawStarts, object.multiDrawCounts, object.multiDrawCount, object.multiDrawInstances! );
+ }
+ else {
+ if ( ! extensions.get( 'WEBGL_multi_draw' ) ) {
+ final starts = object.multiDrawStarts;
+ final counts = object.multiDrawCounts;
+ final drawCount = object.multiDrawCount;
+ final bytesPerElement = index != null? attributes.get( index ).bytesPerElement : 1;
+ final uniforms = properties.get( material )['currentProgram'].getUniforms();
+
+ for ( int i = 0; i < drawCount; i ++ ) {
+ uniforms.setValue( _gl, '_gl_DrawID', i );
+ renderer.render( starts[ i ] ~/ bytesPerElement, counts[ i ] );
+ }
+ }
+ else {
+ renderer.renderMultiDraw( object.multiDrawStarts, object.multiDrawCounts, object.multiDrawCount );
+ }
+ }
+ }
+ if (object is InstancedMesh) {
+ renderer.renderInstances(drawStart, drawCount, object.count!);
+ }
+ else if (geometry is InstancedBufferGeometry) {
+ final instanceCount = math.min(geometry.instanceCount!, geometry.maxInstanceCount ?? 0);
+ renderer.renderInstances(drawStart, drawCount, instanceCount);
+ }
+ else {
+ renderer.render(drawStart, drawCount);
+ }
+ }
+
+ // Compile
+ void prepareMaterial(Material material, Object3D? scene, Object3D object ) {
+
+ if ( material.transparent == true && material.side == DoubleSide && material.forceSinglePass == false ) {
+
+ material.side = BackSide;
+ material.needsUpdate = true;
+ getProgram( material, scene, object );
+
+ material.side = FrontSide;
+ material.needsUpdate = true;
+ getProgram( material, scene, object );
+
+ material.side = DoubleSide;
+
+ } else {
+
+ getProgram( material, scene, object );
+
+ }
+
+ }
+ Set compile(Object3D scene, Camera camera, [Object3D? targetScene]) {
+ targetScene ??= scene;
+
+ currentRenderState = renderStates.get(targetScene);
+ currentRenderState!.init(camera);
+ renderStateStack.add(currentRenderState!);
+
+ targetScene.traverseVisible((object) {
+ if (object is Light && object.layers.test(camera.layers)) {
+ currentRenderState!.pushLight(object);
+
+ if (object.castShadow) {
+ currentRenderState!.pushShadow(object);
+ }
+ }
+ });
+
+ if ( scene != targetScene ) {
+ scene.traverseVisible((object){
+ if (object is Light && object.layers.test( camera.layers ) ) {
+ currentRenderState!.pushLight( object );
+ if ( object.castShadow ) {
+ currentRenderState!.pushShadow( object );
+ }
+ }
+ });
+ }
+ currentRenderState!.setupLights();
+
+ final materials = Set();
+
+ scene.traverse((object) {
+ if( ! ( object is Mesh || object is Points || object is Line || object is Sprite ) ) {
+ return;
+ }
+
+ final material = object.material;
+
+ if (material != null) {
+ if (material is GroupMaterial) {
+ for (int i = 0; i < material.children.length; i++) {
+ final material2 = material.children[i];
+ prepareMaterial(material2, targetScene, object);//getProgram(material2, scene, object);
+ materials.add( material2 );
+ }
+ } else {
+ prepareMaterial(material, targetScene, object);//getProgram(material, scene, object);
+ materials.add( material );
+ }
+ }
+ });
+
+ currentRenderState = renderStateStack.removeLast();
+ return materials;
+ }
+
+ // Animation Loop
+
+ void Function(double)? onAnimationFrameCallback;
+
+ void onAnimationFrame(double time) {
+ if (onAnimationFrameCallback != null) onAnimationFrameCallback!(time);
+ }
+
+ void onXRSessionStart(event) {
+ animation.stop();
+ }
+
+ void onXRSessionEnd(event) {
+ animation.start();
+ }
+
+ void setAnimationLoop( callback ) {
+ onAnimationFrameCallback = callback;
+ xr.setAnimationLoop( callback );
+ ( callback == null ) ? animation.stop() : animation.start();
+ }
+ // Rendering
+
+ @override
+ void render(Object3D scene, Camera camera) {
+
+ if (_isContextLost) return;
+
+ // update scene graph
+ if (scene.matrixWorldAutoUpdate) scene.updateMatrixWorld();
+
+ // update camera matrices and frustum
+
+ if (camera.parent == null && camera.matrixWorldAutoUpdate) camera.updateMatrixWorld();
+
+ if ( xr.enabled && xr.isPresenting ) {
+ if (xr.cameraAutoUpdate) xr.updateCamera( camera );
+ if(kIsWeb) camera = xr.getCamera();
+ }
+
+ if (scene is Scene) {
+ scene.onBeforeRender?.call(renderer: this, scene: scene, camera: camera, renderTarget: _currentRenderTarget);
+ }
+
+ currentRenderState = renderStates.get(scene, renderCallDepth: renderStateStack.length);
+ currentRenderState!.init(camera);
+
+ renderStateStack.add(currentRenderState!);
+
+ projScreenMatrix.multiply2(camera.projectionMatrix, camera.matrixWorldInverse);
+
+ _frustum.setFromMatrix(projScreenMatrix);
+
+ _localClippingEnabled = localClippingEnabled;
+ _clippingEnabled = clipping.init(clippingPlanes, _localClippingEnabled);
+
+ currentRenderList = renderLists.get(scene, renderListStack.length);
+ currentRenderList!.init();
+
+ renderListStack.add(currentRenderList!);
+
+ if ( xr.enabled && xr.isPresenting) {
+ final depthSensingMesh = xr.getDepthSensingMesh();
+ if ( depthSensingMesh != null ) {
+ projectObject( depthSensingMesh, camera, - double.maxFinite.toInt(), this.sortObjects );
+ }
+ }
+
+ projectObject(scene, camera, 0, sortObjects);
+
+ currentRenderList!.finish();
+
+ if (sortObjects) {
+ currentRenderList!.sort(_opaqueSort, _transparentSort);
+ }
+
+ renderBackground = !xr.enabled || !xr.isPresenting || !xr.hasDepthSensing();
+ if ( renderBackground ) {
+ background.addToRenderList( currentRenderList!, scene );
+ }
+
+ info.render['frame'] = info.render['frame']!+1;
+
+ if (_clippingEnabled) clipping.beginShadows();
+ final shadowsArray = currentRenderState!.state.shadowsArray;
+ if(kIsWeb){
+ shadowMap.render(shadowsArray, scene, camera);
+ }
+ if (_clippingEnabled) clipping.endShadows();
+
+ if (info.autoReset) info.reset();
+
+ // render scene
+ final opaqueObjects = currentRenderList?.opaque;
+ final transmissiveObjects = currentRenderList?.transmissive;
+
+ currentRenderState!.setupLights(physicallyCorrectLights);
+
+ if (camera is ArrayCamera) {
+
+ final cameras = camera.cameras;
+ if (transmissiveObjects != null && transmissiveObjects.isNotEmpty) {
+ for (int i = 0, l = cameras.length; i < l; i ++ ) {
+ final camera2 = cameras[ i ];
+ renderTransmissionPass( opaqueObjects!, transmissiveObjects, scene, camera2);
+ }
+ }
+ if (renderBackground) background.render(scene);
+ for (int i = 0, l = cameras.length; i < l; i++) {
+ final camera2 = cameras[i];
+ renderScene(currentRenderList!, scene, camera2, camera2.viewport);
+ }
+ }
+ else {
+ if ( renderBackground ) background.render( scene );
+ renderScene(currentRenderList!, scene, camera);
+ if(transmissiveObjects != null && transmissiveObjects.isNotEmpty) renderTransmissionPass( opaqueObjects!, transmissiveObjects, scene, camera );
+ }
+
+ if(!kIsWeb){
+ shadowMap.render(shadowsArray, scene, camera);
+ }
+
+ if (_currentRenderTarget != null) {
+ // resolve multisample renderbuffers to a single-sample texture if necessary
+ textures.updateMultisampleRenderTarget(_currentRenderTarget!);
+ // Generate mipmap if we're using any kind of mipmap filtering
+ textures.updateRenderTargetMipmap(_currentRenderTarget!);
+ }
+
+ if (scene is Scene) {
+ scene.onAfterRender?.call(renderer: this, scene: scene, camera: camera);
+ }
+
+ _gl.flush();
+
+ bindingStates.resetDefaultState();
+ _currentMaterialId = -1;
+ _currentCamera = null;
+
+ renderStateStack.removeLast();
+ if (renderStateStack.isNotEmpty) {
+ currentRenderState = renderStateStack[renderStateStack.length - 1];
+ if (_clippingEnabled) clipping.setGlobalState(clippingPlanes, currentRenderState!.state.camera! );
+ }
+ else {
+ currentRenderState = null;
+ }
+
+ renderListStack.removeLast();
+
+ if (renderListStack.isNotEmpty) {
+ currentRenderList = renderListStack[renderListStack.length - 1];
+ }
+ else {
+ currentRenderList = null;
+ }
+ }
+
+ void projectObject(Object3D object, Camera camera, int groupOrder, bool sortObjects) {
+ if (!object.visible) return;
+ final visible = object.layers.test(camera.layers);
+
+ if (visible) {
+ if (object is Group) {
+ groupOrder = object.renderOrder;
+ }
+ else if (object is LOD) {
+ dynamic u = object;
+ if (object.autoUpdate == true) u.update(camera);
+ }
+ else if (object is Light) {
+ currentRenderState!.pushLight(object);
+
+ if (object.castShadow) {
+ currentRenderState!.pushShadow(object);
+ }
+ }
+ else if (object is Sprite) {
+ if (!object.frustumCulled || _frustum.intersectsSprite(object)) {
+ if (sortObjects) {
+ _vector4.setFromMatrixPosition(object.matrixWorld).applyMatrix4(projScreenMatrix);
+ }
+
+ BufferGeometry geometry = objects.update(object);
+ final material = object.material;
+
+ if (material != null && material.visible) {
+ currentRenderList!.push(object, geometry, material, groupOrder, _vector4.z, null);
+ }
+ }
+ }
+ else if (object is Mesh || object is Line || object is Points) {
+ // if (object is SkinnedMesh) {
+ // // update skeleton only once in a frame
+ // if (object.skeleton!.frame != info.render["frame"]) {
+ // object.skeleton!.update();
+ // object.skeleton!.frame = info.render["frame"]!;
+ // }
+ // }
+
+ // print("object: ${object.type} ${!object.frustumCulled} ${_frustum.intersectsObject(object)} ");
+
+ if (!object.frustumCulled || _frustum.intersectsObject(object)) {
+ final geometry = objects.update(object);
+ final material = object.material;
+
+ if (sortObjects) {
+ if (object.boundingSphere != null ) {
+ if (object.boundingSphere == null ) object.computeBoundingSphere();
+ _vector4.setFrom(object.boundingSphere!.center );
+ }
+ else {
+ if ( geometry.boundingSphere == null ) geometry.computeBoundingSphere();
+ _vector4.setFrom( geometry.boundingSphere!.center );
+ }
+ _vector4..applyMatrix4(object.matrixWorld)..applyMatrix4(projScreenMatrix);
+ }
+
+ if (material is GroupMaterial) {
+ final groups = geometry.groups;
+
+ if (groups.isNotEmpty) {
+ for (int i = 0, l = groups.length; i < l; i++) {
+ Map group = groups[i];
+ final groupMaterial = material.children[group["materialIndex"]];
+
+ if (groupMaterial.visible) {
+ currentRenderList!.push(object, geometry, groupMaterial, groupOrder, _vector4.z, group);
+ }
+ }
+ }
+ else {
+ if (material.visible && material.children.isNotEmpty) {
+ currentRenderList!.push(object, geometry, material.children[0], groupOrder, _vector4.z, null);
+ }
+ }
+ }
+ else if (material != null && material.visible) {
+ currentRenderList!.push(object, geometry, material, groupOrder, _vector4.z, null);
+ }
+ }
+ }
+ }
+
+ final children = object.children;
+
+ for (int i = 0, l = children.length; i < l; i++) {
+ projectObject(children[i], camera, groupOrder, sortObjects);
+ }
+ }
+
+ void renderScene(AngleRenderList currentRenderList, Object3D scene, Camera camera, [Vector4? viewport]) {
+ List opaqueObjects = currentRenderList.opaque;
+ final transmissiveObjects = currentRenderList.transmissive;
+ final transparentObjects = currentRenderList.transparent;
+
+ currentRenderState!.setupLightsView(camera);
+
+ if (_clippingEnabled) clipping.setGlobalState(clippingPlanes, camera );
+
+ if (viewport != null){
+ _currentViewport.setFrom(viewport);
+ state.viewport(_currentViewport);
+ }
+
+ if (opaqueObjects.isNotEmpty) renderObjects(opaqueObjects, scene, camera);
+ if (transmissiveObjects.isNotEmpty) renderObjects(transmissiveObjects, scene, camera);
+ if (transparentObjects.isNotEmpty) renderObjects(transparentObjects, scene, camera);
+
+ // Ensure depth buffer writing is enabled so it can be cleared on next render
+
+ state.buffers["depth"].setTest(true);
+ state.buffers["depth"].setMask(true);
+ state.buffers["color"].setMask(true);
+
+ (state as AngleState).setPolygonOffset(false);
+ }
+
+ void renderTransmissionPass(List opaqueObjects, List transmissiveObjects, Object3D scene, Camera camera) {
+ final overrideMaterial = scene is Scene? scene.overrideMaterial : null;
+
+ if ( overrideMaterial != null ) {
+ return;
+ }
+
+ RenderTarget? transmissionRenderTarget = currentRenderState?.state.transmissionRenderTarget[ camera.id ];
+ final activeViewport = camera.viewport ?? _currentViewport;
+
+ if ( currentRenderState?.state.transmissionRenderTarget[ camera.id ] == null ||
+ (activeViewport.w.toInt() != transmissionRenderTarget?.height || activeViewport.z.toInt() != transmissionRenderTarget?.width)
+ ) {
+ transmissionRenderTarget?.dispose();
+ currentRenderState?.state.transmissionRenderTarget[ camera.id ] = RenderTarget( 1, 1, RenderTargetOptions({
+ 'generateMipmaps': true,
+ 'type': ( extensions.has( 'EXT_color_buffer_half_float' ) || extensions.has( 'EXT_color_buffer_float' ) ) ? HalfFloatType : UnsignedByteType,
+ 'minFilter': LinearMipmapLinearFilter,
+ 'samples': 4,
+ 'stencilBuffer': stencil,
+ 'resolveDepthBuffer': false,
+ 'resolveStencilBuffer': false,
+ 'colorSpace': ColorManagement.workingColorSpace.toString(),
+ }));
+
+ transmissionRenderTarget = currentRenderState?.state.transmissionRenderTarget[ camera.id ];
+ }
+
+ transmissionRenderTarget!.setSize( (activeViewport.z * transmissionResolutionScale).toInt(), (activeViewport.w * transmissionResolutionScale).toInt());
+
+ final currentRenderTarget = getRenderTarget();
+ setRenderTarget( transmissionRenderTarget );
+
+ getClearColor( currentClearColor );
+ currentClearAlpha = getClearAlpha();
+ if ( currentClearAlpha < 1 ) setClearColor( Color.fromHex32(0xffffff), 0.5 );
+ clear();
+
+ if ( renderBackground ) background.render( scene );
+
+ // Turn off the features which can affect the frag color for opaque objects pass.
+ // Otherwise they are applied twice in opaque objects pass and transmission objects pass.
+ final currentToneMapping = toneMapping;
+ toneMapping = NoToneMapping;
+
+ // Remove viewport from camera to avoid nested render calls resetting viewport to it (e.g Reflector).
+ // Transmission render pass requires viewport to match the transmissionRenderTarget.
+ final currentCameraViewport = camera.viewport;
+ if ( camera.viewport != null ) camera.viewport = null;
+
+ currentRenderState?.setupLightsView( camera );
+
+ if ( _clippingEnabled == true ) clipping.setGlobalState( clippingPlanes, camera );
+
+ renderObjects( opaqueObjects, scene, camera );
+
+ textures.updateMultisampleRenderTarget( transmissionRenderTarget );
+ textures.updateRenderTargetMipmap( transmissionRenderTarget );
+
+ if (!extensions.has( 'WEBGL_multisampled_render_to_texture' ) ) { // see #28131
+ bool renderTargetNeedsUpdate = false;
+
+ for ( int i = 0, l = transmissiveObjects.length; i < l; i ++ ) {
+ final renderItem = transmissiveObjects[ i ];
+
+ final object = renderItem.object;
+ final geometry = renderItem.geometry;
+ final material = renderItem.material;
+ final group = renderItem.group;
+
+ if ( material!.side == DoubleSide && object!.layers.test( camera.layers ) ) {
+ final currentSide = material.side;
+
+ material.side = BackSide;
+ material.needsUpdate = true;
+
+ renderObject( object, scene, camera, geometry!, material, group );
+
+ material.side = currentSide;
+ material.needsUpdate = true;
+
+ renderTargetNeedsUpdate = true;
+ }
+ }
+
+ if ( renderTargetNeedsUpdate == true ) {
+ textures.updateMultisampleRenderTarget( transmissionRenderTarget );
+ textures.updateRenderTargetMipmap( transmissionRenderTarget );
+ }
+ }
+
+ setRenderTarget( currentRenderTarget );
+ setClearColor( currentClearColor, currentClearAlpha );
+
+ if (currentCameraViewport != null) camera.viewport = currentCameraViewport;
+
+ toneMapping = currentToneMapping;
+ }
+
+ void renderObjects(List renderList, Object3D scene, Camera camera) {
+ final overrideMaterial = scene is Scene ? scene.overrideMaterial : null;
+ for (int i = 0, l = renderList.length; i < l; i++) {
+ final renderItem = renderList[i];
+
+ final object = renderItem.object!;
+ final geometry = renderItem.geometry!;
+ final material = overrideMaterial ?? renderItem.material!;
+ final group = renderItem.group;
+
+ if (object.layers.test(camera.layers)) {
+ renderObject(object, scene, camera, geometry, material, group);
+ }
+ }
+ }
+
+ void renderObject(Object3D object, scene, Camera camera, BufferGeometry geometry, Material material, Map? group) {
+ object.onBeforeRender?.call(
+ renderer: this,
+ mesh: object,
+ scene: scene,
+ camera: camera,
+ geometry: geometry,
+ material: material,
+ group: group
+ );
+
+ object.modelViewMatrix.multiply2(camera.matrixWorldInverse, object.matrixWorld);
+ object.normalMatrix.getNormalMatrix(object.modelViewMatrix);
+
+ material.onBeforeRender?.call(
+ this,
+ scene,
+ camera,
+ geometry,
+ object,
+ group
+ );
+
+ if (material.transparent && material.side == DoubleSide && !material.forceSinglePass) {
+ material.side = BackSide;
+ material.needsUpdate = true;
+ renderBufferDirect(camera, scene, geometry, material, object, group);
+
+ material.side = FrontSide;
+ material.needsUpdate = true;
+ renderBufferDirect(camera, scene, geometry, material, object, group);
+
+ material.side = DoubleSide;
+ }
+ else {
+ renderBufferDirect(camera, scene, geometry, material, object, group);
+ }
+
+ object.onAfterRender?.call(renderer: this, scene: scene, camera: camera, geometry: geometry, material: material, group: group);
+ }
+
+ AngleProgram? getProgram(Material material, Object3D? scene, Object3D object) {
+ if (scene is! Scene) scene = _emptyScene;
+ // scene could be a Mesh, Line, Points, ...
+
+ final materialProperties = properties.get(material);
+
+ final lights = currentRenderState!.state.lights;
+ final shadowsArray = currentRenderState!.state.shadowsArray;
+
+ final lightsStateVersion = lights.state.version;
+
+ final parameters = programCache.getParameters(material, lights.state, shadowsArray, scene, object);
+ final programCacheKey = programCache.getProgramCacheKey(parameters);
+
+ Map? programs = materialProperties["programs"];
+
+ // always update environment and fog - changing these trigger an getProgram call, but it's possible that the program doesn't change
+ materialProperties["environment"] = material is MeshStandardMaterial ? scene.environment : null;
+ materialProperties["fog"] = scene.fog;
+ materialProperties['envMap'] = ( material is MeshStandardMaterial ? cubeuvmaps.get( material.envMap ?? materialProperties['environment'] ) : cubemaps.get( material.envMap ?? materialProperties['environment'] ) );
+ materialProperties['envMapRotation'] = ( materialProperties['environment'] != null && material.envMap == null ) ? scene.environmentRotation : material.envMapRotation;
+
+ if (programs == null) {
+ material.addEventListener('dispose', onMaterialDispose);
+ programs = {};
+ materialProperties["programs"] = programs;
+ }
+
+ AngleProgram? program = programs[programCacheKey];
+
+ if (program != null) {
+ // early out if program and light state is identical
+ if (materialProperties["currentProgram"] == program && materialProperties["lightsStateVersion"] == lightsStateVersion) {
+ updateCommonMaterialProperties(material, parameters);
+ return program;
+ }
+ }
+ else {
+ parameters.uniforms = programCache.getUniforms(material);
+
+ //material.onBuild(parameters, this);
+ material.onBeforeCompile?.call(parameters, this);
+ program = programCache.acquireProgram(parameters, programCacheKey);
+ programs[programCacheKey] = program;
+
+ materialProperties["uniforms"] = parameters.uniforms;
+ }
+
+ Map uniforms = materialProperties["uniforms"];
+
+ if ((material is! ShaderMaterial && material is! RawShaderMaterial) || material.clipping == true) {
+ uniforms["clippingPlanes"] = clipping.uniform;
+ }
+
+ updateCommonMaterialProperties(material, parameters);
+
+ // store the light setup it was created for
+
+ materialProperties["needsLights"] = materialNeedsLights(material);
+ materialProperties["lightsStateVersion"] = lightsStateVersion;
+
+ if (materialProperties["needsLights"] == true) {
+ // wire up the material to this renderer's lighting state
+
+ uniforms["ambientLightColor"]["value"] = lights.state.ambient;
+ uniforms["lightProbe"]["value"] = lights.state.probe;
+ uniforms["directionalLights"]["value"] = lights.state.directional;
+ uniforms["directionalLightShadows"]["value"] = lights.state.directionalShadow;
+ uniforms["spotLights"]["value"] = lights.state.spot;
+ uniforms["spotLightShadows"]["value"] = lights.state.spotShadow;
+ uniforms["rectAreaLights"]["value"] = lights.state.rectArea;
+ uniforms["ltc_1"]["value"] = lights.state.rectAreaLTC1;
+ uniforms["ltc_2"]["value"] = lights.state.rectAreaLTC2;
+ uniforms["pointLights"]["value"] = lights.state.point;
+ uniforms["pointLightShadows"]["value"] = lights.state.pointShadow;
+ uniforms["hemisphereLights"]["value"] = lights.state.hemi;
+
+ uniforms["directionalShadowMap"]["value"] = lights.state.directionalShadowMap;
+ uniforms["directionalShadowMatrix"]["value"] = lights.state.directionalShadowMatrix;
+ uniforms["spotShadowMap"]["value"] = lights.state.spotShadowMap;
+ uniforms["spotLightMatrix"]["value"] = lights.state.spotLightMatrix;
+ uniforms["spotLightMap"]["value"] = lights.state.spotLightMap;
+ uniforms["pointShadowMap"]["value"] = lights.state.pointShadowMap;
+ uniforms["pointShadowMatrix"]["value"] = lights.state.pointShadowMatrix;
+ }
+
+ materialProperties["currentProgram"] = program;
+ materialProperties["uniformsList"] = null;
+
+ return program;
+ }
+
+ List getUniformList(Map materialProperties ) {
+ if ( materialProperties['uniformsList'] == null ) {
+ final progUniforms = (materialProperties['currentProgram'] as AngleProgram).getUniforms();
+ materialProperties['uniformsList'] = AngleUniforms.seqWithValue( progUniforms.seq, materialProperties['uniforms'] );
+ }
+
+ return materialProperties['uniformsList'];
+ }
+ void updateCommonMaterialProperties(Material material, Parameters parameters) {
+ final materialProperties = properties.get(material);
+
+ materialProperties['outputColorSpace'] = parameters.outputColorSpace;
+ materialProperties['batching'] = parameters.batching;
+ materialProperties['batchingColor'] = parameters.batchingColor;
+ materialProperties['instancing'] = parameters.instancing;
+ materialProperties['instancingColor'] = parameters.instancingColor;
+ materialProperties['instancingMorph'] = parameters.instancingMorph;
+ materialProperties['skinning'] = parameters.skinning;
+ materialProperties['morphTargets'] = parameters.morphTargets;
+ materialProperties['morphNormals'] = parameters.morphNormals;
+ materialProperties['morphColors'] = parameters.morphColors;
+ materialProperties['morphTargetsCount'] = parameters.morphTargetsCount;
+ materialProperties['numClippingPlanes'] = parameters.numClippingPlanes;
+ materialProperties['numIntersection'] = parameters.numClipIntersection;
+ materialProperties['vertexAlphas'] = parameters.vertexAlphas;
+ materialProperties['vertexTangents'] = parameters.vertexTangents;
+ materialProperties['toneMapping'] = parameters.toneMapping;
+ }
+
+ AngleProgram setProgram(Camera camera, Object3D? scene, BufferGeometry? geometry, Material material, Object3D object) {
+ if (scene is! Scene) scene = _emptyScene; // scene could be a Mesh, Line, Points, ...
+ textures.resetTextureUnits();
+
+ final fog = scene.fog;
+ final environment = material is MeshStandardMaterial ? scene.environment : null;
+ final colorSpace = ( _currentRenderTarget == null ) ? outputColorSpace : ( _currentRenderTarget?.isXRRenderTarget == true ? _currentRenderTarget?.texture.colorSpace : LinearSRGBColorSpace );
+ final envMap = ( material is MeshStandardMaterial ? cubeuvmaps.get( material.envMap ?? environment ) : cubemaps.get( material.envMap ?? environment ) );
+ final vertexAlphas = material.vertexColors &&
+ geometry?.attributes['color'] != null &&
+ geometry?.attributes['color'].itemSize == 4;
+ final vertexTangents = geometry?.attributes['tangent'] != null && (material.normalMap != null || (material is MeshPhysicalMaterial && material.anisotropy > 0));
+ final morphTargets = geometry?.morphAttributes['position'] != null;
+ final morphNormals = geometry?.morphAttributes['normal'] != null;
+ final morphColors = geometry?.morphAttributes['color'] != null;
+
+ int toneMapping = NoToneMapping;
+
+ if ( material.toneMapped ) {
+ if ( _currentRenderTarget == null || _currentRenderTarget?.isXRRenderTarget == true ) {
+ toneMapping = toneMapping;
+ }
+ }
+
+ final morphAttribute = geometry?.morphAttributes['position'] ?? geometry?.morphAttributes['normal'] ?? geometry?.morphAttributes['color'];
+ final morphTargetsCount = ( morphAttribute != null ) ? morphAttribute.length : 0;
+
+ final materialProperties = properties.get( material );
+ final lights = currentRenderState?.state.lights;
+
+ if (_clippingEnabled) {
+ if (_localClippingEnabled || camera != _currentCamera ) {
+ final useCache = camera == _currentCamera && material.id == _currentMaterialId;
+
+ // we might want to call this function with some ClippingGroup
+ // object instead of the material, once it becomes feasible
+ // (#8465, #8379)
+ clipping.setState( material, camera, useCache );
+ }
+ }
+
+ bool needsProgramChange = false;
+
+ if ( material.version == materialProperties['__version'] ) {
+ if ( materialProperties['needsLights'] != null && ( materialProperties['lightsStateVersion'] != lights?.state.version ) ) {
+ needsProgramChange = true;
+ } else if ( materialProperties['outputColorSpace'] != colorSpace ) {
+ needsProgramChange = true;
+ } else if ( object is BatchedMesh && materialProperties['batching'] == false ) {
+ needsProgramChange = true;
+ } else if (object is! BatchedMesh && materialProperties['batching'] == true ) {
+ needsProgramChange = true;
+ }else if ( object is BatchedMesh && materialProperties['batchingColor'] == true && object.colorsTexture == null ) {
+ needsProgramChange = true;
+ } else if ( object is BatchedMesh && materialProperties['batchingColor'] == false && object.colorsTexture != null ) {
+ needsProgramChange = true;
+ }else if ( object is InstancedMesh && materialProperties['instancing'] == false ) {
+ needsProgramChange = true;
+ } else if (object is! InstancedMesh && materialProperties['instancing'] == true ) {
+ needsProgramChange = true;
+ } else if ( object is SkinnedMesh && materialProperties['skinning'] == false ) {
+ needsProgramChange = true;
+ } else if (object is! SkinnedMesh && materialProperties['skinning'] == true ) {
+ needsProgramChange = true;
+ } else if ( object is InstancedMesh && materialProperties['instancingColor'] == true && object.instanceColor == null ) {
+ needsProgramChange = true;
+ } else if ( object is InstancedMesh && materialProperties['instancingColor'] == false && object.instanceColor != null ) {
+ needsProgramChange = true;
+ } else if ( object is InstancedMesh && materialProperties['instancingMorph'] == true && object.morphTexture == null ) {
+ needsProgramChange = true;
+ } else if ( object is InstancedMesh && materialProperties['instancingMorph'] == false && object.morphTexture != null ) {
+ needsProgramChange = true;
+ } else if ( materialProperties['envMap'] != envMap ) {
+ needsProgramChange = true;
+ } else if ( material.fog == true && materialProperties['fog'] != fog ) {
+ needsProgramChange = true;
+ } else if ( materialProperties['numClippingPlanes'] != null &&
+ ( materialProperties['numClippingPlanes'] != clipping.numPlanes ||
+ materialProperties['numIntersection'] != clipping.numIntersection ) ) {
+ needsProgramChange = true;
+ } else if ( materialProperties['vertexAlphas'] != vertexAlphas ) {
+ needsProgramChange = true;
+ } else if ( materialProperties['vertexTangents'] != vertexTangents ) {
+ needsProgramChange = true;
+ } else if ( materialProperties['morphTargets'] != morphTargets ) {
+ needsProgramChange = true;
+ } else if ( materialProperties['morphNormals'] != morphNormals ) {
+ needsProgramChange = true;
+ } else if ( materialProperties['morphColors'] != morphColors ) {
+ needsProgramChange = true;
+ } else if ( materialProperties['toneMapping'] != toneMapping ) {
+ needsProgramChange = true;
+ } else if ( materialProperties['morphTargetsCount'] != morphTargetsCount ) {
+ needsProgramChange = true;
+ }
+ } else {
+ needsProgramChange = true;
+ materialProperties['__version'] = material.version;
+ }
+
+ AngleProgram? program = materialProperties['currentProgram'];
+
+ if (needsProgramChange) {
+ program = getProgram( material, scene, object );
+ }
+
+ bool refreshProgram = false;
+ bool refreshMaterial = false;
+ bool refreshLights = false;
+
+ final AngleUniforms? pUniformS = program?.getUniforms();
+ final Map mUniformS = materialProperties['uniforms'];
+
+ if ((state as AngleState).useProgram( program?.program ) ) {
+ refreshProgram = true;
+ refreshMaterial = true;
+ refreshLights = true;
+ }
+
+ if ( material.id != _currentMaterialId ) {
+ _currentMaterialId = material.id;
+ refreshMaterial = true;
+ }
+
+ if ( refreshProgram || _currentCamera != camera ) {
+
+ // common camera uniforms
+ final reverseDepthBuffer = (state.buffers['depth'] as DepthBuffer).getReversed();
+
+ if ( reverseDepthBuffer ) {
+ _currentProjectionMatrix.setFrom( camera.projectionMatrix );
+ toNormalizedProjectionMatrix( _currentProjectionMatrix );
+ toReversedProjectionMatrix( _currentProjectionMatrix );
+ pUniformS?.setValue( _gl, 'projectionMatrix', _currentProjectionMatrix );
+ }
+ else {
+ pUniformS?.setValue( _gl, 'projectionMatrix', camera.projectionMatrix );
+ }
+
+ pUniformS?.setValue( _gl, 'viewMatrix', camera.matrixWorldInverse );
+
+ final uCamPos = pUniformS?.map['cameraPosition'];
+
+ if ( uCamPos != null ) {
+ uCamPos.setValue( _gl, _vector3.setFromMatrixPosition( camera.matrixWorld ) );
+ }
+
+ if ( capabilities.logarithmicDepthBuffer ) {
+ pUniformS?.setValue( _gl, 'logDepthBufFC', 2.0 / ( math.log( camera.far + 1.0 ) / math.ln2 ) );
+ }
+
+ // consider moving isOrthographic to UniformLib and WebGLMaterials, see https://github.com/mrdoob/three.js/pull/26467#issuecomment-1645185067
+
+ if ( material is MeshPhongMaterial ||
+ material is MeshToonMaterial ||
+ material is MeshLambertMaterial ||
+ material is MeshBasicMaterial ||
+ material is MeshStandardMaterial ||
+ material is ShaderMaterial ) {
+ pUniformS?.setValue( _gl, 'isOrthographic', camera is OrthographicCamera);
+ }
+
+ if ( _currentCamera != camera ) {
+ _currentCamera = camera;
+
+ // lighting uniforms depend on the camera so enforce an update
+ // now, in case this material supports lights - or later, when
+ // the next material that does gets activated:
+
+ refreshMaterial = true; // set to true on material change
+ refreshLights = true; // remains set until update done
+ }
+ }
+
+ // skinning and morph target uniforms must be set even if material didn't change
+ // auto-setting of texture unit for bone and morph texture must go before other textures
+ // otherwise textures used for skinning and morphing can take over texture units reserved for other material textures
+
+ if ( object is SkinnedMesh ) {
+ pUniformS?.setOptional( _gl, object, 'bindMatrix' );
+ pUniformS?.setOptional( _gl, object, 'bindMatrixInverse' );
+
+ final skeleton = object.skeleton;
+ if ( skeleton != null) {
+ if ( skeleton.boneTexture == null ) skeleton.computeBoneTexture();
+ pUniformS?.setValue( _gl, 'boneTexture', skeleton.boneTexture, textures );
+ }
+ }
+
+ if ( object is BatchedMesh ) {
+ pUniformS?.setOptional( _gl, object, 'batchingTexture' );
+ pUniformS?.setValue( _gl, 'batchingTexture', object.matricesTexture, textures );
+
+ pUniformS?.setOptional( _gl, object, 'batchingIdTexture' );
+ pUniformS?.setValue( _gl, 'batchingIdTexture', object.indirectTexture, textures );
+
+ pUniformS?.setOptional( _gl, object, 'batchingColorTexture' );
+ if ( object.colorsTexture != null ) {
+ pUniformS?.setValue( _gl, 'batchingColorTexture', object.colorsTexture, textures );
+ }
+ }
+
+ final morphAttributes = geometry?.morphAttributes;
+
+ if ( morphAttributes?['position'] != null || morphAttributes?['normal'] != null || ( morphAttributes?['color'] != null ) ) {
+ morphtargets.update( object, geometry!, program! );
+ }
+
+ if ( refreshMaterial || materialProperties['receiveShadow'] != object.receiveShadow ) {
+ materialProperties['receiveShadow'] = object.receiveShadow;
+ pUniformS?.setValue( _gl, 'receiveShadow', object.receiveShadow );
+ }
+
+ // https://github.com/mrdoob/three.js/pull/24467#issuecomment-1209031512
+
+ if ( material is MeshGouraudMaterial && material.envMap != null ) {
+ mUniformS['envMap']['value'] = envMap;
+ mUniformS['flipEnvMap']['value'] = ( envMap is CubeTexture && envMap.isRenderTargetTexture == false ) ? - 1 : 1;
+ }
+
+ if ( material is MeshStandardMaterial && material.envMap == null && scene.environment != null ) {
+ mUniformS['envMapIntensity']['value'] = scene.environmentIntensity;
+ }
+
+ if ( refreshMaterial ) {
+ pUniformS?.setValue( _gl, 'toneMappingExposure', toneMappingExposure );
+ if ( materialProperties['needsLights'] == true) {
+ markUniformsLightsNeedsUpdate( mUniformS, refreshLights );
+ }
+
+ // refresh uniforms common to several materials
+
+ if (fog != null && material.fog == true ) {
+ materials.refreshFogUniforms( mUniformS, fog );
+ }
+
+ materials.refreshMaterialUniforms( mUniformS, material, _pixelRatio, _height, currentRenderState?.state.transmissionRenderTarget[ camera.id ] );
+ AngleUniforms.upload( _gl, getUniformList( materialProperties ), mUniformS, textures );
+ }
+
+ if ( material is ShaderMaterial && material.uniformsNeedUpdate == true ) {
+ AngleUniforms.upload( _gl, getUniformList( materialProperties ), mUniformS, textures );
+ material.uniformsNeedUpdate = false;
+ }
+
+ if ( material is SpriteMaterial ) {
+ pUniformS?.setValue( _gl, 'center', (object as Sprite).center );
+ }
+
+ // common matrices
+
+ pUniformS?.setValue( _gl, 'modelViewMatrix', object.modelViewMatrix );
+ pUniformS?.setValue( _gl, 'normalMatrix', object.normalMatrix );
+ pUniformS?.setValue( _gl, 'modelMatrix', object.matrixWorld );
+
+ // UBOs
+
+ if ( material is ShaderMaterial || material is RawShaderMaterial ) {
+ late final List groups;
+ if ( material is ShaderMaterial) {
+ groups = material.uniformsGroups;
+ }
+ else if(material is RawShaderMaterial){
+ groups = material.uniformsGroups;
+ }
+
+ for ( int i = 0, l = groups.length; i < l; i ++ ) {
+ final group = groups[i];
+ uniformsGroups.update( group, program );
+ uniformsGroups.bind( group, program );
+ }
+ }
+
+ return program!;
+ }
+
+ void markUniformsLightsNeedsUpdate(Map uniforms, dynamic value) {
+ uniforms["ambientLightColor"]["needsUpdate"] = value;
+ uniforms["lightProbe"]["needsUpdate"] = value;
+ uniforms["directionalLights"]["needsUpdate"] = value;
+ uniforms["directionalLightShadows"]["needsUpdate"] = value;
+ uniforms["pointLights"]["needsUpdate"] = value;
+ uniforms["pointLightShadows"]["needsUpdate"] = value;
+ uniforms["spotLights"]["needsUpdate"] = value;
+ uniforms["spotLightShadows"]["needsUpdate"] = value;
+ uniforms["rectAreaLights"]["needsUpdate"] = value;
+ uniforms["hemisphereLights"]["needsUpdate"] = value;
+ }
+
+ bool materialNeedsLights(Material material) {
+ return material is MeshLambertMaterial ||
+ material is MeshToonMaterial ||
+ material is MeshPhongMaterial ||
+ material is MeshStandardMaterial ||
+ material is ShadowMaterial ||
+ (material is ShaderMaterial && material.lights == true);
+ }
+
+ int getActiveCubeFace() {
+ return _currentActiveCubeFace;
+ }
+
+ int getActiveMipmapLevel() {
+ return _currentActiveMipmapLevel;
+ }
+
+ @override
+ RenderTarget? getRenderTarget() {
+ return _currentRenderTarget;
+ }
+
+ void setRenderTargetTextures(RenderTarget renderTarget, colorTexture, depthTexture) {
+ properties.get(renderTarget.texture)["__webglTexture"] = colorTexture;
+ properties.get(renderTarget.depthTexture)["__webglTexture"] = depthTexture;
+
+ final renderTargetProperties = properties.get(renderTarget);
+ renderTargetProperties["__hasExternalTextures"] = true;
+
+ //if (renderTargetProperties["__hasExternalTextures"] == true) {
+ renderTargetProperties["__autoAllocateDepthBuffer"] = depthTexture == null;
+
+ if (!(renderTargetProperties["__autoAllocateDepthBuffer"] == true)) {
+ if (extensions.has('WEBGL_multisampled_render_to_texture') == true) {
+ console.warning('WebGLRenderer: extension was disabled because an external texture was provided');
+ renderTargetProperties['__useRenderToTexture'] = false;
+ }
+ }
+ //}
+ }
+
+ void setRenderTargetFramebuffer(RenderTarget renderTarget, Framebuffer? defaultFramebuffer) {
+ final renderTargetProperties = properties.get(renderTarget);
+ renderTargetProperties["__webglFramebuffer"] = defaultFramebuffer;
+ renderTargetProperties["__useDefaultFramebuffer"] = defaultFramebuffer == null;
+ }
+
+ @override
+ void setRenderTarget(RenderTarget? renderTarget, [int activeCubeFace = 0, int activeMipmapLevel = 0]) {
+ _currentRenderTarget = renderTarget;
+ _currentActiveCubeFace = activeCubeFace;
+ _currentActiveMipmapLevel = activeMipmapLevel;
+
+ bool useDefaultFramebuffer = true;
+ Framebuffer? framebuffer;
+ bool isCube = false;
+ bool isRenderTarget3D = false;
+
+ if (renderTarget != null) {
+ final renderTargetProperties = properties.get(renderTarget);
+
+ if (renderTargetProperties["__useDefaultFramebuffer"] != null) {
+ // We need to make sure to rebind the framebuffer.
+ (state as AngleState).bindFramebuffer(WebGL.FRAMEBUFFER, null);
+ useDefaultFramebuffer = false;
+ }
+ else if (renderTargetProperties["__webglFramebuffer"] == null) {
+ textures.setupRenderTarget(renderTarget);
+ }
+ else if (renderTargetProperties["__hasExternalTextures"] == true) {
+ // Color and depth texture must be rebound in order for the swapchain to update.
+ textures.rebindTextures(renderTarget, properties.get(renderTarget.texture)["__webglTexture"],properties.get(renderTarget.depthTexture)["__webglTexture"]);
+ }else if ( renderTarget.depthBuffer ) {
+ // check if the depth texture is already bound to the frame buffer and that it's been initialized
+ final depthTexture = renderTarget.depthTexture;
+ if ( renderTargetProperties['__boundDepthTexture'] != depthTexture ) {
+
+ // check if the depth texture is compatible
+ if (
+ depthTexture != null &&
+ properties.has( depthTexture ) &&
+ ( renderTarget.width != depthTexture.image.width || renderTarget.height != depthTexture.image.height )
+ ) {
+ throw( 'WebGLRenderTarget: Attached DepthTexture is initialized to the incorrect size.' );
+ }
+
+ // Swap the depth buffer to the currently attached one
+ textures.setupDepthRenderbuffer( renderTarget );
+ }
+ }
+
+ final texture = renderTarget.texture;
+
+ if (texture is Data3DTexture || texture is DataArrayTexture || texture is CompressedArrayTexture) {
+ isRenderTarget3D = true;
+ }
+
+ final webglFramebuffer = properties.get(renderTarget)["__webglFramebuffer"];
+
+ if (renderTarget is CubeRenderTarget) {
+ if (webglFramebuffer[ activeCubeFace ] is List) {
+ framebuffer = webglFramebuffer[ activeCubeFace ][ activeMipmapLevel ];
+ }
+ else {
+ framebuffer = webglFramebuffer[ activeCubeFace ];
+ }
+ isCube = true;
+ }
+ else if ((renderTarget.samples > 0) && textures.useMultisampledRTT(renderTarget) == false) {
+ framebuffer = properties.get(renderTarget)["__webglMultisampledFramebuffer"];
+ }
+ else {
+ if (webglFramebuffer is List) {
+ framebuffer = webglFramebuffer[ activeMipmapLevel ];
+ } else {
+ framebuffer = webglFramebuffer;
+ }
+ }
+
+ _currentViewport.setFrom(renderTarget.viewport);
+ _currentScissor.setFrom(renderTarget.scissor);
+ _currentScissorTest = renderTarget.scissorTest;
+ }
+ else {
+ _currentViewport.setFrom(_viewport).scale(_pixelRatio).floor();
+ _currentScissor.setFrom(_scissor).scale(_pixelRatio).floor();
+ _currentScissorTest = _scissorTest;
+ }
+
+ // Use a scratch frame buffer if rendering to a mip level to avoid depth buffers
+ // being bound that are different sizes.
+ if ( activeMipmapLevel != 0 ) {
+ framebuffer = _scratchFrameBuffer;
+ }
+ final angleState = state as AngleState;
+ final framebufferBound = angleState.bindFramebuffer(WebGL.FRAMEBUFFER, framebuffer);
+
+ if (framebufferBound && capabilities.drawBuffers && useDefaultFramebuffer) {
+ angleState.drawBuffers(renderTarget, framebuffer);
+ }
+
+ angleState.viewport(_currentViewport);
+ angleState.scissor(_currentScissor);
+ angleState.setScissorTest(_currentScissorTest!);
+
+ if (isCube) {
+ final textureProperties = properties.get(renderTarget!.texture);
+ _gl.framebufferTexture2D(WebGL.FRAMEBUFFER, WebGL.COLOR_ATTACHMENT0, WebGL.TEXTURE_CUBE_MAP_POSITIVE_X + activeCubeFace, textureProperties["__webglTexture"], activeMipmapLevel);
+ }
+ else if (isRenderTarget3D) {
+ final textureProperties = properties.get(renderTarget!.texture);
+ final layer = activeCubeFace;
+ _gl.framebufferTextureLayer( WebGL.FRAMEBUFFER, WebGL.COLOR_ATTACHMENT0, textureProperties["__webglTexture"], activeMipmapLevel, layer);
+ }
+ else if ( renderTarget != null && activeMipmapLevel != 0 ) {
+ // Only bind the frame buffer if we are using a scratch frame buffer to render to a mipmap.
+ // If we rebind the texture when using a multi sample buffer then an error about inconsistent samples will be thrown.
+ final textureProperties = properties.get( renderTarget.texture );
+ _gl.framebufferTexture2D( WebGL.FRAMEBUFFER, WebGL.COLOR_ATTACHMENT0, WebGL.TEXTURE_2D, textureProperties['__webglTexture'], activeMipmapLevel );
+ }
+
+ _currentMaterialId = -1; // reset current material to ensure correct uniform bindings
+ }
+
+ void readRenderTargetPixels(RenderTarget renderTarget, int x, int y, int width, int height, TypedData buffer, [int? activeCubeFaceIndex]) {
+ dynamic framebuffer = properties.get(renderTarget)["__webglFramebuffer"]; //can be Map or int
+
+ if (renderTarget is CubeRenderTarget && activeCubeFaceIndex != null) {
+ framebuffer = framebuffer?[activeCubeFaceIndex];
+ }
+
+ if (framebuffer != null) {
+ (state as AngleState).bindFramebuffer(WebGL.FRAMEBUFFER, framebuffer);
+
+ try {
+ final texture = renderTarget.texture;
+ final textureFormat = texture.format;
+ final textureType = texture.type;
+
+ if (textureFormat != RGBAFormat &&
+ utils.convert(textureFormat) != _gl.getParameter(WebGL.IMPLEMENTATION_COLOR_READ_FORMAT)) {
+ console.warning('WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.');
+ return;
+ }
+
+ final halfFloatSupportedByExt = textureType == HalfFloatType &&
+ (extensions.has('EXT_color_buffer_half_float') ||
+ (capabilities.isWebGL2 && extensions.has('EXT_color_buffer_float')));
+
+ if (textureType != UnsignedByteType &&
+ (kIsWeb && utils.convert(textureType) != _gl.getParameter(WebGL.IMPLEMENTATION_COLOR_READ_TYPE)) && // IE11, Edge and Chrome Mac < 52 (#9513)
+ !(textureType == FloatType &&
+ (capabilities.isWebGL2 ||
+ extensions.get('OES_texture_float') ||
+ extensions.get('WEBGL_color_buffer_float'))) && // Chrome Mac >= 52 and Firefox
+ !halfFloatSupportedByExt) {
+ console.warning('WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.');
+ return;
+ }
+ // the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604)
+
+ if ((x >= 0 && x <= (renderTarget.width - width)) && (y >= 0 && y <= (renderTarget.height - height))) {
+ _gl.readPixels(x, y, width, height, utils.convert(textureFormat), utils.convert(textureType), buffer);
+ }
+ } finally {
+ final framebuffer = (_currentRenderTarget != null) ? properties.get(_currentRenderTarget)["__webglFramebuffer"] : null;
+ (state as AngleState).bindFramebuffer(WebGL.FRAMEBUFFER, framebuffer);
+ }
+ }
+ }
+
+ @override
+ void copyFramebufferToTexture(Vector? position, Texture? texture, {int level = 0}) {
+ //console.warning('copyFramebufferToTexture not supported');
+ if (texture is! FramebufferTexture) {
+ console.warning('WebGLRenderer: copyFramebufferToTexture() can only be used with FramebufferTexture.');
+ return;
+ }
+
+ final levelScale = math.pow(2, -level);
+ final width = (texture.image.width * levelScale).floor();
+ final height = (texture.image.height * levelScale).floor();
+
+ final x = position != null && !position.x.isNaN ? position.x.toInt() : 0;
+ final y = position != null && !position.y.isNaN ? position.y.toInt() : 0;
+
+ textures.setTexture2D(texture, 0);
+ _gl.copyTexSubImage2D(WebGL.TEXTURE_2D, level, 0, 0, x, y, width, height);
+ (state as AngleState).unbindTexture(WebGLTexture(WebGL.TEXTURE_2D));
+ }
+
+ void copyTextureToTexture(Texture srcTexture, Texture dstTexture, {srcRegion, dstPosition, int srcLevel = 0, dstLevel}) {
+ if ( dstLevel == null ) {
+ if ( srcLevel != 0 ) {
+ dstLevel = srcLevel;
+ srcLevel = 0;
+ }
+ else {
+ dstLevel = 0;
+ }
+ }
+
+ // gather the necessary dimensions to copy
+ int width, height, depth, minX, minY, minZ;
+ int dstX, dstY, dstZ;
+ final image = srcTexture is CompressedTexture ? srcTexture.mipmaps[ dstLevel ] : srcTexture.image;
+ if ( srcRegion != null ) {
+ width = srcRegion.max.x - srcRegion.min.x;
+ height = srcRegion.max.y - srcRegion.min.y;
+ depth = srcRegion is BoundingBox ? (srcRegion.max.z - srcRegion.min.z).toInt() : 1;
+ minX = srcRegion.min.x;
+ minY = srcRegion.min.y;
+ minZ = srcRegion is BoundingBox ? srcRegion.min.z.toInt() : 0;
+ }
+ else {
+ final levelScale = math.pow( 2, - srcLevel );
+ width = ( image.width * levelScale ).floor();
+ height = ( image.height * levelScale ).floor();
+ if ( srcTexture is DataArrayTexture ) {
+ depth = image.depth;
+ }
+ else if ( srcTexture is Data3DTexture ) {
+ depth = ( image.depth * levelScale ).floor();
+ }
+ else {
+ depth = 1;
+ }
+
+ minX = 0;
+ minY = 0;
+ minZ = 0;
+ }
+
+ if ( dstPosition != null ) {
+ dstX = dstPosition.x;
+ dstY = dstPosition.y;
+ dstZ = dstPosition.z;
+ }
+ else {
+ dstX = 0;
+ dstY = 0;
+ dstZ = 0;
+ }
+
+ // Set up the destination target
+ final glFormat = utils.convert( dstTexture.format );
+ final glType = utils.convert( dstTexture.type );
+ int glTarget = 0;
+
+ if ( dstTexture is Data3DTexture ) {
+ textures.setTexture3D( dstTexture, 0 );
+ glTarget = WebGL.TEXTURE_3D;
+ }
+ else if ( dstTexture is DataArrayTexture || dstTexture is CompressedArrayTexture ) {
+ textures.setTexture2DArray( dstTexture, 0 );
+ glTarget = WebGL.TEXTURE_2D_ARRAY;
+ }
+ else {
+ textures.setTexture2D( dstTexture, 0 );
+ glTarget = WebGL.TEXTURE_2D;
+ }
+ // if(kIsWeb){
+ // _gl.pixelStorei( WebGL.UNPACK_FLIP_Y_WEBGL, dstTexture.flipY?1:0 );
+ // _gl.pixelStorei( WebGL.UNPACK_PREMULTIPLY_ALPHA_WEBGL, dstTexture.premultiplyAlpha?1:0 );
+ // _gl.pixelStorei( WebGL.UNPACK_ALIGNMENT, dstTexture.unpackAlignment );
+ // }
+
+ // used for copying data from cpu
+ final currentUnpackRowLen = _gl.getParameter( WebGL.UNPACK_ROW_LENGTH );
+ final currentUnpackImageHeight = _gl.getParameter( WebGL.UNPACK_IMAGE_HEIGHT );
+ final currentUnpackSkipPixels = _gl.getParameter( WebGL.UNPACK_SKIP_PIXELS );
+ final currentUnpackSkipRows = _gl.getParameter( WebGL.UNPACK_SKIP_ROWS );
+ final currentUnpackSkipImages = _gl.getParameter( WebGL.UNPACK_SKIP_IMAGES );
+
+ _gl.pixelStorei( WebGL.UNPACK_ROW_LENGTH, image.width );
+ _gl.pixelStorei( WebGL.UNPACK_IMAGE_HEIGHT, image.height );
+ _gl.pixelStorei( WebGL.UNPACK_SKIP_PIXELS, minX );
+ _gl.pixelStorei( WebGL.UNPACK_SKIP_ROWS, minY );
+ _gl.pixelStorei( WebGL.UNPACK_SKIP_IMAGES, minZ );
+
+ // set up the src texture
+ final isSrc3D = srcTexture is DataArrayTexture || srcTexture is Data3DTexture;
+ final isDst3D = dstTexture is DataArrayTexture || dstTexture is Data3DTexture;
+ final angleState = state as AngleState;
+ if ( srcTexture.isDepthTexture ) {
+
+ final srcTextureProperties = properties.get( srcTexture );
+ final dstTextureProperties = properties.get( dstTexture );
+ final srcRenderTargetProperties = properties.get( srcTextureProperties['__renderTarget'] );
+ final dstRenderTargetProperties = properties.get( dstTextureProperties['__renderTarget'] );
+ angleState.bindFramebuffer( WebGL.READ_FRAMEBUFFER, srcRenderTargetProperties['__webglFramebuffer'] );
+ angleState.bindFramebuffer( WebGL.DRAW_FRAMEBUFFER, dstRenderTargetProperties['__webglFramebuffer'] );
+
+ for (int i = 0; i < depth; i ++ ) {
+ // if the source or destination are a 3d target then a layer needs to be bound
+ if ( isSrc3D ) {
+ _gl.framebufferTextureLayer( WebGL.READ_FRAMEBUFFER, WebGL.COLOR_ATTACHMENT0, properties.get( srcTexture )['__webglTexture'], srcLevel, minZ + i );
+ _gl.framebufferTextureLayer( WebGL.DRAW_FRAMEBUFFER, WebGL.COLOR_ATTACHMENT0, properties.get( dstTexture )['__webglTexture'], dstLevel, dstZ + i );
+ }
+
+ _gl.blitFramebuffer( minX, minY, width, height, dstX, dstY, width, height, WebGL.DEPTH_BUFFER_BIT, WebGL.NEAREST );
+ }
+
+ angleState.bindFramebuffer( WebGL.READ_FRAMEBUFFER, null );
+ angleState.bindFramebuffer( WebGL.DRAW_FRAMEBUFFER, null );
+
+ }
+ else if ( srcLevel != 0 || srcTexture.isRenderTargetTexture || properties.has( srcTexture ) ) {
+ // get the appropriate frame buffers
+ final srcTextureProperties = properties.get( srcTexture );
+ final dstTextureProperties = properties.get( dstTexture );
+
+ // bind the frame buffer targets
+ angleState.bindFramebuffer( WebGL.READ_FRAMEBUFFER, _srcFramebuffer );
+ angleState.bindFramebuffer( WebGL.DRAW_FRAMEBUFFER, _dstFramebuffer );
+
+ for (int i = 0; i < depth; i ++ ) {
+
+ // assign the correct layers and mip maps to the frame buffers
+ if ( isSrc3D ) {
+ _gl.framebufferTextureLayer( WebGL.READ_FRAMEBUFFER, WebGL.COLOR_ATTACHMENT0, srcTextureProperties['__webglTexture'], srcLevel, minZ + i );
+ }
+ else {
+ _gl.framebufferTexture2D( WebGL.READ_FRAMEBUFFER, WebGL.COLOR_ATTACHMENT0, WebGL.TEXTURE_2D, srcTextureProperties['__webglTexture'], srcLevel );
+ }
+
+ if ( isDst3D ) {
+ _gl.framebufferTextureLayer( WebGL.DRAW_FRAMEBUFFER, WebGL.COLOR_ATTACHMENT0, dstTextureProperties['__webglTexture'], dstLevel, dstZ + i );
+ }
+ else {
+ _gl.framebufferTexture2D( WebGL.DRAW_FRAMEBUFFER, WebGL.COLOR_ATTACHMENT0, WebGL.TEXTURE_2D, dstTextureProperties['__webglTexture'], dstLevel );
+ }
+
+ // copy the data using the fastest function that can achieve the copy
+ if ( srcLevel != 0 ) {
+ _gl.blitFramebuffer( minX, minY, width, height, dstX, dstY, width, height, WebGL.COLOR_BUFFER_BIT, WebGL.NEAREST );
+ } else if ( isDst3D ) {
+ _gl.copyTexSubImage3D( glTarget, dstLevel, dstX, dstY, dstZ + i, minX, minY, width, height );
+ } else {
+ _gl.copyTexSubImage2D( glTarget, dstLevel, dstX, dstY, minX, minY, width, height );
+ }
+ }
+
+ // unbind read, draw buffers
+ angleState.bindFramebuffer( WebGL.READ_FRAMEBUFFER, null );
+ angleState.bindFramebuffer( WebGL.DRAW_FRAMEBUFFER, null );
+ }
+ else {
+
+ if ( isDst3D ) {
+ // copy data into the 3d texture
+ if ( srcTexture is DataTexture || srcTexture is Data3DTexture ) {
+ _gl.texSubImage3D( glTarget, dstLevel, dstX, dstY, dstZ, width, height, depth, glFormat, glType, image.data );
+ }
+ else if ( dstTexture is CompressedArrayTexture ) {
+ _gl.compressedTexSubImage3D( glTarget, dstLevel, dstX, dstY, dstZ, width, height, depth, glFormat, image.data );
+ }
+ else {
+ _gl.texSubImage3D( glTarget, dstLevel, dstX, dstY, dstZ, width, height, depth, glFormat, glType, image );
+ }
+ }
+ else {
+ // copy data into the 2d texture
+ if ( srcTexture is DataTexture ) {
+ _gl.texSubImage2D( WebGL.TEXTURE_2D, dstLevel, dstX, dstY, width, height, glFormat, glType, image.data );
+ }
+ else if ( srcTexture.isCompressedTexture ) {
+ _gl.compressedTexSubImage2D( WebGL.TEXTURE_2D, dstLevel, dstX, dstY, image.width, image.height, glFormat, image.data );
+ }
+ else {
+ _gl.texSubImage2D( WebGL.TEXTURE_2D, dstLevel, dstX, dstY, width, height, glFormat, glType, image );
+ }
+ }
+ }
+
+ // reset values
+ _gl.pixelStorei( WebGL.UNPACK_ROW_LENGTH, currentUnpackRowLen );
+ _gl.pixelStorei( WebGL.UNPACK_IMAGE_HEIGHT, currentUnpackImageHeight );
+ _gl.pixelStorei( WebGL.UNPACK_SKIP_PIXELS, currentUnpackSkipPixels );
+ _gl.pixelStorei( WebGL.UNPACK_SKIP_ROWS, currentUnpackSkipRows );
+ _gl.pixelStorei( WebGL.UNPACK_SKIP_IMAGES, currentUnpackSkipImages );
+
+ // Generate mipmaps only when copying level 0
+ if ( dstLevel == 0 && dstTexture.generateMipmaps ) {
+ _gl.generateMipmap( glTarget );
+ }
+
+ angleState.unbindTexture();
+ }
+
+ void copyTextureToTexture3D(
+ Texture srcTexture,
+ Texture dstTexture, {
+ srcRegion,
+ dstPosition,
+ int level = 0,
+ }) {
+ return copyTextureToTexture( srcTexture, dstTexture, srcRegion: srcRegion, dstPosition: dstPosition, srcLevel: level);
+ }
+
+ void initRenderTarget( target ) {
+ if ( properties.get( target )['__webglFramebuffer'] == null ) {
+ textures.setupRenderTarget( target );
+ }
+ }
+
+ void initTexture(Texture texture) {
+ if (texture is CubeTexture) {
+ textures.setTextureCube( texture, 0 );
+ }
+ else if (texture is Data3DTexture ) {
+ textures.setTexture3D( texture, 0 );
+ }
+ else if ( texture is DataArrayTexture || texture is CompressedArrayTexture ) {
+ textures.setTexture2DArray( texture, 0 );
+ }
+ else{
+ textures.setTexture2D(texture, 0);
+ }
+
+ (state as AngleState).unbindTexture();
+ }
+
+ WebGLTexture getRenderTargetGLTexture(RenderTarget renderTarget) {
+ final textureProperties = properties.get(renderTarget.texture);
+ return textureProperties["__webglTexture"];
+ }
+
+ void resetState() {
+ _currentActiveCubeFace = 0;
+ _currentActiveMipmapLevel = 0;
+ _currentRenderTarget = null;
+
+ state.reset();
+ bindingStates.reset();
+ }
+
+ @override
+ void setOutputColorSpace(String colorSpace ) {
+ super.setOutputColorSpace(colorSpace);
+
+ final gl = this.getContext();
+ gl.drawingBufferColorSpace = colorSpace == DisplayP3ColorSpace ? 'display-p3' : 'srgb';
+ gl.unpackColorSpace = ColorManagement.workingColorSpace == LinearDisplayP3ColorSpace ? 'display-p3' : 'srgb';
+ }
+
+ void toNormalizedProjectionMatrix(Matrix4 projectionMatrix ) {
+ final m = projectionMatrix.storage;
+
+ // Convert [-1, 1] to [0, 1] projection matrix
+ m[ 2 ] = 0.5 * m[ 2 ] + 0.5 * m[ 3 ];
+ m[ 6 ] = 0.5 * m[ 6 ] + 0.5 * m[ 7 ];
+ m[ 10 ] = 0.5 * m[ 10 ] + 0.5 * m[ 11 ];
+ m[ 14 ] = 0.5 * m[ 14 ] + 0.5 * m[ 15 ];
+ }
+
+ void toReversedProjectionMatrix(Matrix4 projectionMatrix ) {
+ final m = projectionMatrix.storage;
+ final isPerspectiveMatrix = m[ 11 ] == - 1;
+
+ // Reverse [0, 1] projection matrix
+ if ( isPerspectiveMatrix ) {
+ m[ 10 ] = - m[ 10 ] - 1;
+ m[ 14 ] = - m[ 14 ];
+ }
+ else {
+ m[ 10 ] = - m[ 10 ];
+ m[ 14 ] = - m[ 14 ] + 1;
+ }
+ }
+}
diff --git a/packages/three_js_angle_renderer/lib/shaders/index.dart b/packages/three_js_angle_renderer/lib/shaders/index.dart
new file mode 100755
index 00000000..d609c506
--- /dev/null
+++ b/packages/three_js_angle_renderer/lib/shaders/index.dart
@@ -0,0 +1,5 @@
+export 'shader_lib/vsm_vert.glsl.dart';
+export 'shader_lib/vsm_frag.glsl.dart';
+export 'shader_chunk.dart';
+export 'shader_chunk/lights_fragment_begin.glsl.dart';
+export 'shader_chunk/lights_pars_begin.glsl.dart';
\ No newline at end of file
diff --git a/packages/three_js_angle_renderer/lib/shaders/mesh_gouraund_material.dart b/packages/three_js_angle_renderer/lib/shaders/mesh_gouraund_material.dart
new file mode 100644
index 00000000..ac0e4781
--- /dev/null
+++ b/packages/three_js_angle_renderer/lib/shaders/mesh_gouraund_material.dart
@@ -0,0 +1,305 @@
+import "package:three_js_math/three_js_math.dart";
+import "package:three_js_core/three_js_core.dart";
+
+final Map gouraudShader = {
+ 'name': 'GouraudShader',
+
+ 'uniforms': UniformsUtils.merge([
+ uniformsLib['common'],
+ uniformsLib['specularmap'],
+ uniformsLib['envmap'],
+ uniformsLib['aomap'],
+ uniformsLib['lightmap'],
+ uniformsLib['emissivemap'],
+ uniformsLib['fog'],
+ uniformsLib['lights'],
+ {
+ 'emissive': { 'value': Color.fromHex32( 0x000000 )}
+ }
+ ]),
+
+ 'vertexShader': /* glsl */'''
+
+ #define GOURAUD
+
+ varying vec3 vLightFront;
+ varying vec3 vIndirectFront;
+
+ #ifdef DOUBLE_SIDED
+ varying vec3 vLightBack;
+ varying vec3 vIndirectBack;
+ #endif
+
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+
+ void main() {
+
+ #include
+ #include
+ #include
+
+ #include
+ #include
+ #include
+ #include
+ #include
+
+ #include