@1pizzateam/roostr 0.8.1 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  [![License: MIT](https://img.shields.io/npm/l/@1pizzateam/roostr.svg)](https://opensource.org/licenses/MIT)
2
2
  [![npm version](https://badge.fury.io/js/%401pizzateam%2Froostr.svg)](https://www.npmjs.com/package/@1pizzateam/roostr)
3
- ![Jest:coverage](https://img.shields.io/badge/Jest:coverage-89%25-brightgreen.svg)
3
+ ![Jest:coverage](https://img.shields.io/badge/Jest:coverage-94%25-brightgreen.svg)
4
4
 
5
5
  ## Synopsis
6
6
 
package/dist/roostr.d.ts CHANGED
@@ -25,7 +25,7 @@ https://github.com/1pizzateam/RoostR.js
25
25
  */
26
26
 
27
27
  import { Mat4x3, Vec3, Quat, Mat3, Box, Sphere as Sphere$1, Vec2, Mat4, Ray3, Frustum, Rect } from '@1pizzateam/spock';
28
- export { Accumulator, Bezier, Box as BoundingBox, Sphere as BoundingSphere, Circ, Frustum, Grid, Mat3, Mat4, Mat4x3, NumArray, Quat, Rand, Ray3 as Ray, Ray2, Rect, RollingAverage, Plane as SpatialPlane, Time, Trigo, Utils, Vec2, Vec3 } from '@1pizzateam/spock';
28
+ export { Accumulator, Bezier, Box as BoundingBox, Sphere as BoundingSphere, Circ, Frustum, GRID_EMPTY_CELL, Grid, Mat3, Mat4, Mat4x3, NumArray, PointSource, Quat, Rand, Ray3 as Ray, Ray2, RayHit2D, Rect, RightTriangle, RollingAverage, Plane as SpatialPlane, Time, TriangleCorner, TriangleShape, Trigo, Utils, Vec, Vec2, Vec3, raycastSegment } from '@1pizzateam/spock';
29
29
 
30
30
  declare class SceneRenderer {
31
31
  context: WebGLRenderingContext;
@@ -148,11 +148,14 @@ declare class GLState {
148
148
  program: WebGLProgram | null;
149
149
  arrayBuffer: WebGLBuffer | null;
150
150
  elementBuffer: WebGLBuffer | null;
151
+ texture2D: WebGLTexture | null;
151
152
  attributeMask: number | null;
152
153
  maxAttributes: number;
153
154
  frameOfProgram: WeakMap<object, number>;
154
155
  materialOfProgram: WeakMap<object, object>;
155
156
  materialVersionOfProgram: WeakMap<object, number>;
157
+ samplerInitializedOfProgram: WeakSet<object>;
158
+ hasTextureOfProgram: WeakMap<object, number>;
156
159
  invalidate(): void;
157
160
  reset(): void;
158
161
  }
@@ -188,6 +191,7 @@ interface IGeometry {
188
191
  primitive: string | null;
189
192
  uvs?: number[] | Float32Array | null;
190
193
  nbSubMeshes?: number;
194
+ bufferUsage?: 'STATIC_DRAW' | 'DYNAMIC_DRAW';
191
195
  quad?: {
192
196
  vertices: number[] | null;
193
197
  indices: number[] | null;
@@ -327,6 +331,7 @@ declare class Mesh extends Node<Mesh> implements IMesh {
327
331
  subMeshes: SubMesh[];
328
332
  nbSubMeshes: number;
329
333
  primitive: string | null;
334
+ bufferUsage: 'STATIC_DRAW' | 'DYNAMIC_DRAW';
330
335
  customUniforms: ICustomUniforms;
331
336
  context: WebGLRenderingContext;
332
337
  renderer: MeshRenderer;
@@ -368,6 +373,7 @@ declare class Mesh extends Node<Mesh> implements IMesh {
368
373
  private applyTextureOption;
369
374
  private releaseTexture;
370
375
  setTexture(img: TextureSource, options?: ITextureOptions): void;
376
+ setSharedTexture(texture: WebGLTexture | null): void;
371
377
  addCustomUniform(name: string, type: string, value: UniformValue): void;
372
378
  setCustomUniform(name: string, value: UniformValue): void;
373
379
  private createIndexData;
@@ -530,7 +536,7 @@ declare class SceneCollector {
530
536
  blendedList: Mesh[];
531
537
  scratchFrustum: Frustum;
532
538
  private scratchVec;
533
- collect(meshes: Mesh[], camera: Camera, frustum: Frustum | null, opaque: boolean, blended: boolean, stamp: number): void;
539
+ collect(meshes: Mesh[], camera: Camera, frustum: Frustum | null, opaque: boolean, blended: boolean, stamp: number, maxDrawDistance?: number): void;
534
540
  drawBlended(scene: Scene, camera: Camera, time: number, flatLights: IFlatLights, frame: IFrameInfo): number;
535
541
  }
536
542
 
@@ -552,6 +558,7 @@ interface ISceneOptions {
552
558
  far: number;
553
559
  } | null;
554
560
  enableFrustumCulling?: boolean;
561
+ maxDrawDistance?: number;
555
562
  }
556
563
  declare class Scene {
557
564
  meshes: Mesh[];
@@ -564,6 +571,7 @@ declare class Scene {
564
571
  far: number;
565
572
  } | null;
566
573
  enableFrustumCulling: boolean;
574
+ maxDrawDistance: number;
567
575
  context: WebGLRenderingContext;
568
576
  renderer: SceneRenderer;
569
577
  graph: SceneGraph;
@@ -734,6 +742,27 @@ declare class OrbitControls {
734
742
  update(deltaTime?: number): void;
735
743
  }
736
744
 
745
+ interface IQuadBatchOptions {
746
+ capacity: number;
747
+ vertexShader?: string;
748
+ fragmentShader?: string;
749
+ material?: Material;
750
+ texture?: TextureSource;
751
+ textureOptions?: ITextureOptions;
752
+ sharedTexture?: WebGLTexture | null;
753
+ }
754
+ declare class QuadBatch extends Mesh {
755
+ capacity: number;
756
+ used: number;
757
+ bounds: Box;
758
+ private scratchPoint;
759
+ constructor(context: WebGLRenderingContext, options: IQuadBatchOptions);
760
+ begin(): this;
761
+ quad(corners: ArrayLike<number>, uv: ArrayLike<number>): boolean;
762
+ end(): this;
763
+ show(visible: boolean): void;
764
+ }
765
+
737
766
  declare class BasicMesh implements IGeometry {
738
767
  vertices: number[] | Float32Array | null;
739
768
  indices: number[] | Int32Array | Uint16Array | null;
@@ -821,6 +850,7 @@ declare class CustomMesh implements IGeometry {
821
850
  itemSize: number;
822
851
  primitive: string;
823
852
  primitives: string[];
853
+ bufferUsage: 'STATIC_DRAW' | 'DYNAMIC_DRAW';
824
854
  constructor();
825
855
  private static toFloat32;
826
856
  setVertices(array: Vec3[]): void;
@@ -932,10 +962,5 @@ declare const NormalShader: {
932
962
  create(context: WebGLRenderingContext): WebGLProgram | null;
933
963
  };
934
964
 
935
- type Vec3Like = Vec3 | ArrayLike<number>;
936
- type Vec2Like = Vec2 | ArrayLike<number>;
937
- declare function assignVec3(target: Vec3, xOrVec: Vec3Like | number, y?: number, z?: number): Vec3;
938
- declare function assignVec2(target: Vec2, xOrVec: Vec2Like | number, y?: number): Vec2;
939
-
940
- export { AmbientLight, BasicMesh, BasicShader, Camera, Cannon, Cube, CustomMesh, Cylinder, DirectionalLight, FullscreenQuad, Hemisphere, Lights, Line, MAX_LIGHTS, Material, Mesh, MultiQuad, Node, NormalShader, OrbitControls, OrthographicCamera, PerspectiveCamera, PhongShader, Plane, PointLight, Program, Quad, RenderTarget, Renderer, Scene, Shader, Sphere, SpotLight, Sprite, SpriteShader, SubMesh, Texture, Uniform, VWing, assignVec2, assignVec3, eDrawMethod };
941
- export type { AnyLight, ICustomUniforms, IFlatLights, IFrameInfo, IGeometry, IMaterialOptions, IMaterialUniforms, IMesh, IProgram, IRendererOptions, ISceneOptions, ISpriteOptions, ITextureOptions, SpriteAnchor, SpriteBillboard, TextureSource, UniformValue, Vec2Like, Vec3Like };
965
+ export { AmbientLight, BasicMesh, BasicShader, Camera, Cannon, Cube, CustomMesh, Cylinder, DirectionalLight, FullscreenQuad, Hemisphere, Lights, Line, MAX_LIGHTS, Material, Mesh, MultiQuad, Node, NormalShader, OrbitControls, OrthographicCamera, PerspectiveCamera, PhongShader, Plane, PointLight, Program, Quad, QuadBatch, RenderTarget, Renderer, Scene, Shader, Sphere, SpotLight, Sprite, SpriteShader, SubMesh, Texture, Uniform, VWing, eDrawMethod };
966
+ export type { AnyLight, ICustomUniforms, IFlatLights, IFrameInfo, IGeometry, IMaterialOptions, IMaterialUniforms, IMesh, IProgram, IQuadBatchOptions, IRendererOptions, ISceneOptions, ISpriteOptions, ITextureOptions, SpriteAnchor, SpriteBillboard, TextureSource, UniformValue };
package/dist/roostr.js CHANGED
@@ -25,21 +25,25 @@ https://github.com/1pizzateam/RoostR.js
25
25
  */
26
26
 
27
27
  import { Mat4x3, Vec3, Frustum, Vec2, Utils, Time, Mat4, Ray3, Trigo, Quat, Mat3, Box, Sphere as Sphere$1, Rect } from '@1pizzateam/spock';
28
- export { Accumulator, Bezier, Box as BoundingBox, Sphere as BoundingSphere, Circ, Frustum, Grid, Mat3, Mat4, Mat4x3, NumArray, Quat, Rand, Ray3 as Ray, Ray2, Rect, RollingAverage, Plane as SpatialPlane, Time, Trigo, Utils, Vec2, Vec3 } from '@1pizzateam/spock';
28
+ export { Accumulator, Bezier, Box as BoundingBox, Sphere as BoundingSphere, Circ, Frustum, GRID_EMPTY_CELL, Grid, Mat3, Mat4, Mat4x3, NumArray, Quat, Rand, Ray3 as Ray, Ray2, Rect, RightTriangle, RollingAverage, Plane as SpatialPlane, Time, Trigo, Utils, Vec2, Vec3, raycastSegment } from '@1pizzateam/spock';
29
29
 
30
30
  class GLState {
31
31
  program = null;
32
32
  arrayBuffer = null;
33
33
  elementBuffer = null;
34
+ texture2D = null;
34
35
  attributeMask = null;
35
36
  maxAttributes = 0;
36
37
  frameOfProgram = new WeakMap();
37
38
  materialOfProgram = new WeakMap();
38
39
  materialVersionOfProgram = new WeakMap();
40
+ samplerInitializedOfProgram = new WeakSet();
41
+ hasTextureOfProgram = new WeakMap();
39
42
  invalidate() {
40
43
  this.program = null;
41
44
  this.arrayBuffer = null;
42
45
  this.elementBuffer = null;
46
+ this.texture2D = null;
43
47
  this.attributeMask = null;
44
48
  }
45
49
  reset() {
@@ -48,6 +52,8 @@ class GLState {
48
52
  this.frameOfProgram = new WeakMap();
49
53
  this.materialOfProgram = new WeakMap();
50
54
  this.materialVersionOfProgram = new WeakMap();
55
+ this.samplerInitializedOfProgram = new WeakSet();
56
+ this.hasTextureOfProgram = new WeakMap();
51
57
  }
52
58
  }
53
59
  const states = new WeakMap();
@@ -253,7 +259,7 @@ function packLights(directionals, points, spots, ambients, flatArrays, maxLights
253
259
  pushLight(flatArrays, index++, 'point', points[i]);
254
260
  for (let i = 0; i < directionals.length && index < count; i++)
255
261
  pushLight(flatArrays, index++, 'directional', directionals[i]);
256
- const ambient = scratchAmbient.setScalar(0, 0, 0);
262
+ const ambient = scratchAmbient.origin();
257
263
  if (extraAmbient)
258
264
  ambient.add(extraAmbient);
259
265
  for (let i = 0; i < ambients.length; i++)
@@ -261,7 +267,7 @@ function packLights(directionals, points, spots, ambients, flatArrays, maxLights
261
267
  if (!flatArrays.floatAmbient)
262
268
  flatArrays.floatAmbient = new Float32Array(3);
263
269
  ambient.toArray(flatArrays.floatAmbient);
264
- flatArrays.ambient = [ambient.x, ambient.y, ambient.z];
270
+ ambient.toArray(flatArrays.ambient);
265
271
  return flatArrays;
266
272
  }
267
273
 
@@ -408,20 +414,34 @@ class SceneCollector {
408
414
  blendedList = [];
409
415
  scratchFrustum = new Frustum();
410
416
  scratchVec = new Vec3();
411
- collect(meshes, camera, frustum, opaque, blended, stamp) {
417
+ collect(meshes, camera, frustum, opaque, blended, stamp, maxDrawDistance = 0) {
412
418
  this.opaqueList.length = 0;
413
419
  this.blendedList.length = 0;
420
+ const camPos = camera.position;
414
421
  const visit = (mesh) => {
415
422
  if (!mesh.isActive())
416
423
  return;
417
424
  const wanted = mesh.blendMode ? blended : opaque;
418
- if (wanted && !(frustum && !frustum.intersectsSphere(mesh.computeBoundingSphere()))) {
419
- if (mesh.blendMode) {
420
- mesh.sortKey = mesh.getWorldPosition(this.scratchVec).getDistance(camera.position, true);
421
- this.blendedList.push(mesh);
425
+ if (wanted) {
426
+ let visible = true;
427
+ if (frustum || maxDrawDistance > 0) {
428
+ const sphere = mesh.computeBoundingSphere();
429
+ if (maxDrawDistance > 0) {
430
+ const limit = maxDrawDistance + sphere.radius;
431
+ if (sphere.center.getDistance(camPos, true) > limit * limit)
432
+ visible = false;
433
+ }
434
+ if (visible && frustum && !frustum.intersectsSphere(sphere))
435
+ visible = false;
436
+ }
437
+ if (visible) {
438
+ if (mesh.blendMode) {
439
+ mesh.sortKey = mesh.getWorldPosition(this.scratchVec).getDistance(camPos, true);
440
+ this.blendedList.push(mesh);
441
+ }
442
+ else
443
+ this.opaqueList.push(mesh);
422
444
  }
423
- else
424
- this.opaqueList.push(mesh);
425
445
  }
426
446
  for (const child of mesh.children)
427
447
  visit(child);
@@ -458,6 +478,7 @@ class Scene {
458
478
  ambientColor;
459
479
  fog;
460
480
  enableFrustumCulling;
481
+ maxDrawDistance;
461
482
  context;
462
483
  renderer;
463
484
  graph;
@@ -473,6 +494,7 @@ class Scene {
473
494
  this.ambientColor = new Vec3(0, 0, 0);
474
495
  this.fog = null;
475
496
  this.enableFrustumCulling = false;
497
+ this.maxDrawDistance = 0;
476
498
  this.collector = new SceneCollector();
477
499
  let ctx = null;
478
500
  let opts = null;
@@ -490,6 +512,8 @@ class Scene {
490
512
  this.fog = opts.fog;
491
513
  if (opts.enableFrustumCulling !== undefined)
492
514
  this.enableFrustumCulling = opts.enableFrustumCulling;
515
+ if (opts.maxDrawDistance !== undefined)
516
+ this.maxDrawDistance = opts.maxDrawDistance;
493
517
  }
494
518
  else
495
519
  ctx = contextOrOptions;
@@ -649,7 +673,7 @@ class Scene {
649
673
  const frustum = this.enableFrustumCulling
650
674
  ? this.collector.scratchFrustum.setFromProjectionViewMatrix(camera.getProjectionViewMatrix())
651
675
  : null;
652
- this.collector.collect(this.meshes, camera, frustum, true, true, this.traversalStamp);
676
+ this.collector.collect(this.meshes, camera, frustum, true, true, this.traversalStamp, this.maxDrawDistance);
653
677
  const frame = this.beginFrame();
654
678
  this.disableBlendMode();
655
679
  const projMatrix = camera.getProjectionMatrix();
@@ -670,17 +694,19 @@ class Scene {
670
694
  const mark = (mesh) => {
671
695
  for (const child of mesh.children) {
672
696
  child.traversalStamp = stamp;
673
- mark(child);
697
+ if (child.children.length > 0)
698
+ mark(child);
674
699
  }
675
700
  };
676
701
  for (const mesh of this.meshes)
677
- mark(mesh);
702
+ if (mesh.children.length > 0)
703
+ mark(mesh);
678
704
  return stamp;
679
705
  }
680
706
  computeWorldMatrices() {
681
707
  const stamp = this.markDescendants();
682
708
  for (const mesh of this.meshes)
683
- if (mesh.traversalStamp !== stamp)
709
+ if (mesh.traversalStamp !== stamp && mesh.isActive())
684
710
  mesh.computeWorldMatrix(this.graph);
685
711
  }
686
712
  beginFrame() {
@@ -690,7 +716,7 @@ class Scene {
690
716
  return this.frame;
691
717
  }
692
718
  renderBlended(camera, time = Time.now(), flatLights = this.lights.flatten(this.ambientColor), frustum = null) {
693
- this.collector.collect(this.meshes, camera, frustum, false, true, this.markDescendants());
719
+ this.collector.collect(this.meshes, camera, frustum, false, true, this.markDescendants(), this.maxDrawDistance);
694
720
  return this.collector.drawBlended(this, camera, time, flatLights, this.beginFrame());
695
721
  }
696
722
  }
@@ -936,6 +962,7 @@ class RenderTarget {
936
962
  gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, this.width, this.height);
937
963
  gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, this.depthBuffer);
938
964
  gl.bindTexture(gl.TEXTURE_2D, null);
965
+ getGLState(gl).texture2D = null;
939
966
  gl.bindRenderbuffer(gl.RENDERBUFFER, null);
940
967
  gl.bindFramebuffer(gl.FRAMEBUFFER, null);
941
968
  }
@@ -967,6 +994,7 @@ class RenderTarget {
967
994
  gl.bindTexture(gl.TEXTURE_2D, this.texture);
968
995
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.width, this.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
969
996
  gl.bindTexture(gl.TEXTURE_2D, null);
997
+ getGLState(gl).texture2D = null;
970
998
  }
971
999
  if (this.depthBuffer) {
972
1000
  gl.bindRenderbuffer(gl.RENDERBUFFER, this.depthBuffer);
@@ -981,6 +1009,8 @@ class RenderTarget {
981
1009
  this.framebuffer = null;
982
1010
  }
983
1011
  if (this.texture) {
1012
+ if (getGLState(gl).texture2D === this.texture)
1013
+ getGLState(gl).texture2D = null;
984
1014
  gl.deleteTexture(this.texture);
985
1015
  this.texture = null;
986
1016
  }
@@ -1370,7 +1400,7 @@ class OrbitControls {
1370
1400
  const forward = this.camera.getViewDirection(OrbitControls._scratchForward);
1371
1401
  const right = OrbitControls._scratchRight.crossVectors(forward, this.camera.up).normalize();
1372
1402
  const up = OrbitControls._scratchUp.crossVectors(right, forward).normalize();
1373
- OrbitControls._scratchPan.setScalar(0, 0, 0)
1403
+ OrbitControls._scratchPan.origin()
1374
1404
  .add(right.scale(-deltaX * factor))
1375
1405
  .add(up.scale(deltaY * factor));
1376
1406
  this.camera.target.add(OrbitControls._scratchPan);
@@ -1381,8 +1411,7 @@ class OrbitControls {
1381
1411
  const t = deltaTime === undefined
1382
1412
  ? this.dampingFactor
1383
1413
  : 1 - Math.pow(1 - this.dampingFactor, deltaTime * 60);
1384
- const turn = Trigo.normalizeRadian(this.targetAzimuth - this.azimuth);
1385
- this.azimuth = Utils.lerp(this.azimuth, this.azimuth + turn, t);
1414
+ this.azimuth = Trigo.lerpRadian(this.azimuth, this.targetAzimuth, t);
1386
1415
  this.elevation = Utils.lerp(this.elevation, this.targetElevation, t);
1387
1416
  this.distance = Utils.lerp(this.distance, this.targetDistance, t);
1388
1417
  }
@@ -1437,8 +1466,10 @@ class Node {
1437
1466
  this.boundingSphere = new Sphere$1();
1438
1467
  }
1439
1468
  addChild(node) {
1469
+ if (node.parent === this)
1470
+ return;
1440
1471
  if (this.children.indexOf(node) === -1) {
1441
- if (node.parent && node.parent !== this)
1472
+ if (node.parent)
1442
1473
  node.parent.removeChild(node);
1443
1474
  this.children.push(node);
1444
1475
  node.parent = this;
@@ -1475,11 +1506,15 @@ class Node {
1475
1506
  this.markDirty();
1476
1507
  }
1477
1508
  setPosition(position) {
1509
+ if (position !== this.position && this.position.equals(position))
1510
+ return this;
1478
1511
  this.position.copy(position);
1479
1512
  this.markDirty();
1480
1513
  return this;
1481
1514
  }
1482
1515
  setRotation(rotation) {
1516
+ if (rotation !== this._rotation && !this.isRotationStale && this._rotation.equals(rotation))
1517
+ return this;
1483
1518
  this._rotation.copy(rotation);
1484
1519
  this.quaternion.setFromEuler(this._rotation);
1485
1520
  this.isRotationStale = false;
@@ -1487,6 +1522,8 @@ class Node {
1487
1522
  return this;
1488
1523
  }
1489
1524
  setScale(scale) {
1525
+ if (scale !== this.scale && this.scale.equals(scale))
1526
+ return this;
1490
1527
  this.scale.copy(scale);
1491
1528
  this.markDirty();
1492
1529
  return this;
@@ -1497,6 +1534,8 @@ class Node {
1497
1534
  return this;
1498
1535
  }
1499
1536
  setQuaternion(q) {
1537
+ if (q !== this.quaternion && this.quaternion.equals(q))
1538
+ return this;
1500
1539
  this.quaternion.copy(q);
1501
1540
  this.rotationChanged();
1502
1541
  return this;
@@ -1702,6 +1741,7 @@ const Texture = {
1702
1741
  console.warn('Texture: mipmaps skipped, WebGL1 requires power-of-two dimensions.');
1703
1742
  }
1704
1743
  context.bindTexture(context.TEXTURE_2D, null);
1744
+ getGLState(context).texture2D = null;
1705
1745
  return webGLTexture;
1706
1746
  },
1707
1747
  load(url, context, options) {
@@ -1725,6 +1765,7 @@ const Texture = {
1725
1765
  else
1726
1766
  context.texSubImage2D(context.TEXTURE_2D, 0, xOffset, yOffset, context.RGBA, context.UNSIGNED_BYTE, source);
1727
1767
  context.bindTexture(context.TEXTURE_2D, null);
1768
+ getGLState(context).texture2D = null;
1728
1769
  }
1729
1770
  };
1730
1771
 
@@ -1959,15 +2000,24 @@ class MeshRenderer {
1959
2000
  }
1960
2001
  this.setAttributeMask(attributeMask);
1961
2002
  if (mesh.WebGLTexture) {
1962
- context.activeTexture(context.TEXTURE0);
1963
- context.bindTexture(context.TEXTURE_2D, mesh.WebGLTexture);
1964
- if (program.sampler)
2003
+ if (state.texture2D !== mesh.WebGLTexture) {
2004
+ context.activeTexture(context.TEXTURE0);
2005
+ context.bindTexture(context.TEXTURE_2D, mesh.WebGLTexture);
2006
+ state.texture2D = mesh.WebGLTexture;
2007
+ }
2008
+ if (program.sampler && !state.samplerInitializedOfProgram.has(program)) {
1965
2009
  context.uniform1i(program.sampler, 0);
1966
- if (program.hasTexture)
2010
+ state.samplerInitializedOfProgram.add(program);
2011
+ }
2012
+ if (program.hasTexture && state.hasTextureOfProgram.get(program) !== 1) {
1967
2013
  context.uniform1i(program.hasTexture, 1);
2014
+ state.hasTextureOfProgram.set(program, 1);
2015
+ }
1968
2016
  }
1969
- else if (program.hasTexture)
2017
+ else if (program.hasTexture && state.hasTextureOfProgram.get(program) !== 0) {
1970
2018
  context.uniform1i(program.hasTexture, 0);
2019
+ state.hasTextureOfProgram.set(program, 0);
2020
+ }
1971
2021
  if (program.modelMatrix)
1972
2022
  context.uniformMatrix4fv(program.modelMatrix, false, mesh.worldMatrix.toArray());
1973
2023
  if (program.normalMatrix) {
@@ -2359,6 +2409,7 @@ class Mesh extends Node {
2359
2409
  subMeshes;
2360
2410
  nbSubMeshes;
2361
2411
  primitive;
2412
+ bufferUsage;
2362
2413
  customUniforms;
2363
2414
  context;
2364
2415
  renderer;
@@ -2404,6 +2455,7 @@ class Mesh extends Node {
2404
2455
  this.subMeshes = [new SubMesh(0, this.indices ? this.indices.length : this.vertices.length / (this.itemSize || 3))];
2405
2456
  this.nbSubMeshes = this.subMeshes.length;
2406
2457
  this.primitive = mesh && mesh.primitive ? mesh.primitive : null;
2458
+ this.bufferUsage = mesh && mesh.bufferUsage ? mesh.bufferUsage : 'STATIC_DRAW';
2407
2459
  this.customUniforms = {};
2408
2460
  this.context = context;
2409
2461
  this.renderer = new MeshRenderer(this.context);
@@ -2515,17 +2567,18 @@ class Mesh extends Node {
2515
2567
  applyTextureOption(texture) {
2516
2568
  if (!texture)
2517
2569
  return;
2518
- if (typeof WebGLTexture !== 'undefined' && texture instanceof WebGLTexture) {
2519
- this.releaseTexture();
2520
- this.WebGLTexture = texture;
2521
- this.ownsTexture = false;
2522
- }
2570
+ if (typeof WebGLTexture !== 'undefined' && texture instanceof WebGLTexture)
2571
+ this.setSharedTexture(texture);
2523
2572
  else
2524
2573
  this.setTexture(texture);
2525
2574
  }
2526
2575
  releaseTexture() {
2527
- if (this.WebGLTexture && this.ownsTexture)
2528
- this.context.deleteTexture(this.WebGLTexture);
2576
+ if (this.WebGLTexture) {
2577
+ if (this.renderer.state.texture2D === this.WebGLTexture)
2578
+ this.renderer.state.texture2D = null;
2579
+ if (this.ownsTexture)
2580
+ this.context.deleteTexture(this.WebGLTexture);
2581
+ }
2529
2582
  this.WebGLTexture = null;
2530
2583
  this.ownsTexture = false;
2531
2584
  this.textureSource = null;
@@ -2542,6 +2595,15 @@ class Mesh extends Node {
2542
2595
  this.textureOptions = options;
2543
2596
  syncTextureSampler(this.context, this.programs);
2544
2597
  }
2598
+ setSharedTexture(texture) {
2599
+ if (this.WebGLTexture === texture && !this.ownsTexture)
2600
+ return;
2601
+ this.releaseTexture();
2602
+ this.WebGLTexture = texture;
2603
+ this.ownsTexture = false;
2604
+ if (texture)
2605
+ syncTextureSampler(this.context, this.programs);
2606
+ }
2545
2607
  addCustomUniform(name, type, value) {
2546
2608
  if (!this.customUniforms.hasOwnProperty(name)) {
2547
2609
  this.customUniforms[name] = new Uniform(type, value);
@@ -2567,18 +2629,19 @@ class Mesh extends Node {
2567
2629
  return indices instanceof Uint16Array ? indices : new Uint16Array(indices);
2568
2630
  }
2569
2631
  createBuffers() {
2632
+ const usage = this.bufferUsage || 'STATIC_DRAW';
2570
2633
  this.vertexBuffer = this.vertices
2571
- ? this.renderer.createBuffer('ARRAY_BUFFER', this.vertices instanceof Float32Array ? this.vertices : new Float32Array(this.vertices), 'STATIC_DRAW')
2634
+ ? this.renderer.createBuffer('ARRAY_BUFFER', this.vertices instanceof Float32Array ? this.vertices : new Float32Array(this.vertices), usage)
2572
2635
  : null;
2573
2636
  this.indexType = this.context.UNSIGNED_SHORT;
2574
2637
  this.indexBuffer = this.indices
2575
2638
  ? this.renderer.createBuffer('ELEMENT_ARRAY_BUFFER', this.createIndexData(this.indices), 'STATIC_DRAW')
2576
2639
  : null;
2577
2640
  this.normalBuffer = this.normals
2578
- ? this.renderer.createBuffer('ARRAY_BUFFER', this.normals instanceof Float32Array ? this.normals : new Float32Array(this.normals), 'STATIC_DRAW')
2641
+ ? this.renderer.createBuffer('ARRAY_BUFFER', this.normals instanceof Float32Array ? this.normals : new Float32Array(this.normals), usage)
2579
2642
  : null;
2580
2643
  this.texCoordBuffer = this.uvs
2581
- ? this.renderer.createBuffer('ARRAY_BUFFER', this.uvs instanceof Float32Array ? this.uvs : new Float32Array(this.uvs), 'STATIC_DRAW')
2644
+ ? this.renderer.createBuffer('ARRAY_BUFFER', this.uvs instanceof Float32Array ? this.uvs : new Float32Array(this.uvs), usage)
2582
2645
  : null;
2583
2646
  }
2584
2647
  restore() {
@@ -2657,6 +2720,201 @@ class Mesh extends Node {
2657
2720
  }
2658
2721
  }
2659
2722
 
2723
+ class CustomMesh {
2724
+ vertices;
2725
+ indices;
2726
+ normals;
2727
+ uvs;
2728
+ subMeshes;
2729
+ itemSize;
2730
+ primitive;
2731
+ primitives;
2732
+ bufferUsage;
2733
+ constructor() {
2734
+ this.vertices = null;
2735
+ this.indices = null;
2736
+ this.normals = null;
2737
+ this.uvs = null;
2738
+ this.subMeshes = [];
2739
+ this.itemSize = 3;
2740
+ this.primitive = 'TRIANGLES';
2741
+ this.bufferUsage = 'STATIC_DRAW';
2742
+ this.primitives = ['POINTS',
2743
+ 'LINE_STRIP',
2744
+ 'LINE_LOOP',
2745
+ 'LINES',
2746
+ 'TRIANGLE_STRIP',
2747
+ 'TRIANGLE_FAN',
2748
+ 'TRIANGLES'
2749
+ ];
2750
+ }
2751
+ static toFloat32(array) {
2752
+ const flat = new Float32Array(array.length * 3);
2753
+ for (let i = 0; i < array.length; i++)
2754
+ array[i].toArray(flat, i * 3);
2755
+ return flat;
2756
+ }
2757
+ setVertices(array) {
2758
+ this.vertices = CustomMesh.toFloat32(array);
2759
+ }
2760
+ setIndices(array) {
2761
+ this.indices = array instanceof Int32Array ? array.slice(0) : new Int32Array(array);
2762
+ }
2763
+ setNormals(array) {
2764
+ this.normals = CustomMesh.toFloat32(array);
2765
+ }
2766
+ setUvs(array) {
2767
+ this.uvs = array instanceof Float32Array ? array.slice(0) : new Float32Array(array);
2768
+ }
2769
+ addSubMeshes(array) {
2770
+ for (let i = 0; i < array.length; i += 2)
2771
+ this.addSubMesh(array[i], array[i + 1]);
2772
+ }
2773
+ addSubMesh(start, count) {
2774
+ this.subMeshes.push(new SubMesh(start, count));
2775
+ }
2776
+ setItemSize(itemSize) {
2777
+ this.itemSize = itemSize;
2778
+ }
2779
+ setPrimitive(primitive) {
2780
+ for (let i = 0; i < this.primitives.length; i++)
2781
+ if (this.primitives[i] === primitive) {
2782
+ this.primitive = primitive;
2783
+ return true;
2784
+ }
2785
+ return false;
2786
+ }
2787
+ static _vA = new Vec3();
2788
+ static _vB = new Vec3();
2789
+ static _vC = new Vec3();
2790
+ static _e1 = new Vec3();
2791
+ static _e2 = new Vec3();
2792
+ static _n = new Vec3();
2793
+ static _sum = new Vec3();
2794
+ static UP = new Vec3(0, 1, 0);
2795
+ static accumulate(normals, offset, n) {
2796
+ CustomMesh._sum.setArray(normals, offset).add(n).toArray(normals, offset);
2797
+ }
2798
+ computeVertexNormals() {
2799
+ if (!this.vertices || this.vertices.length === 0)
2800
+ return null;
2801
+ const vertices = this.vertices;
2802
+ const normals = new Float32Array(vertices.length);
2803
+ const vA = CustomMesh._vA, vB = CustomMesh._vB, vC = CustomMesh._vC;
2804
+ const e1 = CustomMesh._e1, e2 = CustomMesh._e2, n = CustomMesh._n;
2805
+ const faceNormal = (iA, iB, iC) => {
2806
+ vA.setArray(vertices, iA);
2807
+ vB.setArray(vertices, iB);
2808
+ vC.setArray(vertices, iC);
2809
+ return n.crossVectors(e1.subVectors(vB, vA), e2.subVectors(vC, vA));
2810
+ };
2811
+ if (this.indices && this.indices.length > 0)
2812
+ for (let i = 0; i + 2 < this.indices.length; i += 3) {
2813
+ const iA = this.indices[i] * 3;
2814
+ const iB = this.indices[i + 1] * 3;
2815
+ const iC = this.indices[i + 2] * 3;
2816
+ faceNormal(iA, iB, iC);
2817
+ CustomMesh.accumulate(normals, iA, n);
2818
+ CustomMesh.accumulate(normals, iB, n);
2819
+ CustomMesh.accumulate(normals, iC, n);
2820
+ }
2821
+ else
2822
+ for (let i = 0; i + 8 < vertices.length; i += 9) {
2823
+ faceNormal(i, i + 3, i + 6);
2824
+ n.toArray(normals, i);
2825
+ n.toArray(normals, i + 3);
2826
+ n.toArray(normals, i + 6);
2827
+ }
2828
+ for (let i = 0; i < normals.length; i += 3)
2829
+ n.setArray(normals, i).normalize(CustomMesh.UP).toArray(normals, i);
2830
+ this.normals = normals;
2831
+ return this.normals;
2832
+ }
2833
+ }
2834
+
2835
+ const QUAD_INDICES = [0, 1, 2, 0, 2, 3];
2836
+ class QuadBatch extends Mesh {
2837
+ capacity;
2838
+ used;
2839
+ bounds;
2840
+ scratchPoint;
2841
+ constructor(context, options) {
2842
+ const capacity = options.capacity;
2843
+ const geometry = new CustomMesh();
2844
+ geometry.vertices = new Float32Array(capacity * 18);
2845
+ geometry.uvs = new Float32Array(capacity * 12);
2846
+ geometry.bufferUsage = 'DYNAMIC_DRAW';
2847
+ geometry.setPrimitive('TRIANGLES');
2848
+ super(context, geometry);
2849
+ this.capacity = capacity;
2850
+ this.used = 0;
2851
+ this.bounds = new Box();
2852
+ this.scratchPoint = new Vec3();
2853
+ const vertex = options.vertexShader ?? BasicShader.vertex;
2854
+ const fragment = options.fragmentShader ?? BasicShader.fragment;
2855
+ const material = options.material ?? new Material({ diffuse: new Vec3(1, 1, 1) });
2856
+ this.addProgram(vertex, fragment, material);
2857
+ if (options.sharedTexture)
2858
+ this.setSharedTexture(options.sharedTexture);
2859
+ else if (options.texture)
2860
+ this.setTexture(options.texture, options.textureOptions);
2861
+ this.setInactive();
2862
+ }
2863
+ begin() {
2864
+ this.used = 0;
2865
+ this.bounds.makeEmpty();
2866
+ return this;
2867
+ }
2868
+ quad(corners, uv) {
2869
+ if (this.used >= this.capacity)
2870
+ return false;
2871
+ const u0 = uv[0];
2872
+ const v0 = uv[1];
2873
+ const u1 = uv[2];
2874
+ const v1 = uv[3];
2875
+ const vertices = this.vertices;
2876
+ const uvs = this.uvs;
2877
+ let v = this.used * 18;
2878
+ let t = this.used * 12;
2879
+ for (let c = 0; c < 4; c++)
2880
+ this.bounds.expandByPoint(this.scratchPoint.setArray(corners, c * 3));
2881
+ for (let k = 0; k < 6; k++) {
2882
+ const corner = QUAD_INDICES[k];
2883
+ const i = corner * 3;
2884
+ vertices[v++] = corners[i];
2885
+ vertices[v++] = corners[i + 1];
2886
+ vertices[v++] = corners[i + 2];
2887
+ uvs[t++] = corner === 1 || corner === 2 ? u1 : u0;
2888
+ uvs[t++] = corner >= 2 ? v1 : v0;
2889
+ }
2890
+ this.used++;
2891
+ return true;
2892
+ }
2893
+ end() {
2894
+ const gl = this.context;
2895
+ this.subMeshes[0].count = this.used * 6;
2896
+ if (!this.used) {
2897
+ this.setInactive();
2898
+ return this;
2899
+ }
2900
+ const vertices = this.vertices;
2901
+ const uvs = this.uvs;
2902
+ this.renderer.bindBuffer('ARRAY_BUFFER', this.vertexBuffer);
2903
+ gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.used === this.capacity ? vertices : vertices.subarray(0, this.used * 18));
2904
+ this.renderer.bindBuffer('ARRAY_BUFFER', this.texCoordBuffer);
2905
+ gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.used === this.capacity ? uvs : uvs.subarray(0, this.used * 12));
2906
+ this.setLocalBounds(this.bounds);
2907
+ this.setActive();
2908
+ return this;
2909
+ }
2910
+ show(visible) {
2911
+ if (visible && this.used > 0)
2912
+ this.setActive();
2913
+ else
2914
+ this.setInactive();
2915
+ }
2916
+ }
2917
+
2660
2918
  class BasicMesh {
2661
2919
  vertices;
2662
2920
  indices;
@@ -3107,116 +3365,6 @@ class Cannon extends BasicMesh {
3107
3365
  }
3108
3366
  }
3109
3367
 
3110
- class CustomMesh {
3111
- vertices;
3112
- indices;
3113
- normals;
3114
- uvs;
3115
- subMeshes;
3116
- itemSize;
3117
- primitive;
3118
- primitives;
3119
- constructor() {
3120
- this.vertices = null;
3121
- this.indices = null;
3122
- this.normals = null;
3123
- this.uvs = null;
3124
- this.subMeshes = [];
3125
- this.itemSize = 3;
3126
- this.primitive = 'TRIANGLES';
3127
- this.primitives = ['POINTS',
3128
- 'LINE_STRIP',
3129
- 'LINE_LOOP',
3130
- 'LINES',
3131
- 'TRIANGLE_STRIP',
3132
- 'TRIANGLE_FAN',
3133
- 'TRIANGLES'
3134
- ];
3135
- }
3136
- static toFloat32(array) {
3137
- const flat = new Float32Array(array.length * 3);
3138
- for (let i = 0; i < array.length; i++)
3139
- array[i].toArray(flat, i * 3);
3140
- return flat;
3141
- }
3142
- setVertices(array) {
3143
- this.vertices = CustomMesh.toFloat32(array);
3144
- }
3145
- setIndices(array) {
3146
- this.indices = array instanceof Int32Array ? array.slice(0) : new Int32Array(array);
3147
- }
3148
- setNormals(array) {
3149
- this.normals = CustomMesh.toFloat32(array);
3150
- }
3151
- setUvs(array) {
3152
- this.uvs = array instanceof Float32Array ? array.slice(0) : new Float32Array(array);
3153
- }
3154
- addSubMeshes(array) {
3155
- for (let i = 0; i < array.length; i += 2)
3156
- this.addSubMesh(array[i], array[i + 1]);
3157
- }
3158
- addSubMesh(start, count) {
3159
- this.subMeshes.push(new SubMesh(start, count));
3160
- }
3161
- setItemSize(itemSize) {
3162
- this.itemSize = itemSize;
3163
- }
3164
- setPrimitive(primitive) {
3165
- for (let i = 0; i < this.primitives.length; i++)
3166
- if (this.primitives[i] === primitive) {
3167
- this.primitive = primitive;
3168
- return true;
3169
- }
3170
- return false;
3171
- }
3172
- static _vA = new Vec3();
3173
- static _vB = new Vec3();
3174
- static _vC = new Vec3();
3175
- static _e1 = new Vec3();
3176
- static _e2 = new Vec3();
3177
- static _n = new Vec3();
3178
- static _sum = new Vec3();
3179
- static UP = new Vec3(0, 1, 0);
3180
- static accumulate(normals, offset, n) {
3181
- CustomMesh._sum.setArray(normals, offset).add(n).toArray(normals, offset);
3182
- }
3183
- computeVertexNormals() {
3184
- if (!this.vertices || this.vertices.length === 0)
3185
- return null;
3186
- const vertices = this.vertices;
3187
- const normals = new Float32Array(vertices.length);
3188
- const vA = CustomMesh._vA, vB = CustomMesh._vB, vC = CustomMesh._vC;
3189
- const e1 = CustomMesh._e1, e2 = CustomMesh._e2, n = CustomMesh._n;
3190
- const faceNormal = (iA, iB, iC) => {
3191
- vA.setArray(vertices, iA);
3192
- vB.setArray(vertices, iB);
3193
- vC.setArray(vertices, iC);
3194
- return n.crossVectors(e1.subVectors(vB, vA), e2.subVectors(vC, vA));
3195
- };
3196
- if (this.indices && this.indices.length > 0)
3197
- for (let i = 0; i + 2 < this.indices.length; i += 3) {
3198
- const iA = this.indices[i] * 3;
3199
- const iB = this.indices[i + 1] * 3;
3200
- const iC = this.indices[i + 2] * 3;
3201
- faceNormal(iA, iB, iC);
3202
- CustomMesh.accumulate(normals, iA, n);
3203
- CustomMesh.accumulate(normals, iB, n);
3204
- CustomMesh.accumulate(normals, iC, n);
3205
- }
3206
- else
3207
- for (let i = 0; i + 8 < vertices.length; i += 9) {
3208
- faceNormal(i, i + 3, i + 6);
3209
- n.toArray(normals, i);
3210
- n.toArray(normals, i + 3);
3211
- n.toArray(normals, i + 6);
3212
- }
3213
- for (let i = 0; i < normals.length; i += 3)
3214
- n.setArray(normals, i).normalize(CustomMesh.UP).toArray(normals, i);
3215
- this.normals = normals;
3216
- return this.normals;
3217
- }
3218
- }
3219
-
3220
3368
  const SpriteShader = {
3221
3369
  vertex: `
3222
3370
  attribute vec3 aVertexPosition;
@@ -3632,19 +3780,4 @@ class AmbientLight {
3632
3780
  }
3633
3781
  }
3634
3782
 
3635
- function assignVec3(target, xOrVec, y, z) {
3636
- if (xOrVec instanceof Vec3)
3637
- return target.copy(xOrVec);
3638
- if (typeof xOrVec === 'number')
3639
- return target.setScalar(xOrVec, y, z);
3640
- return target.setArray(xOrVec);
3641
- }
3642
- function assignVec2(target, xOrVec, y) {
3643
- if (xOrVec instanceof Vec2)
3644
- return target.copy(xOrVec);
3645
- if (typeof xOrVec === 'number')
3646
- return target.setScalar(xOrVec, y);
3647
- return target.setArray(xOrVec);
3648
- }
3649
-
3650
- export { AmbientLight, BasicMesh, BasicShader, Camera, Cannon, Cube, CustomMesh, Cylinder, DirectionalLight, FullscreenQuad, Hemisphere, Lights, Line, MAX_LIGHTS, Material, Mesh, MultiQuad, Node, NormalShader, OrbitControls, OrthographicCamera, PerspectiveCamera, PhongShader, Plane, PointLight, Program, Quad, RenderTarget, Renderer, Scene, Shader, Sphere, SpotLight, Sprite, SpriteShader, SubMesh, Texture, Uniform, VWing, assignVec2, assignVec3, eDrawMethod };
3783
+ export { AmbientLight, BasicMesh, BasicShader, Camera, Cannon, Cube, CustomMesh, Cylinder, DirectionalLight, FullscreenQuad, Hemisphere, Lights, Line, MAX_LIGHTS, Material, Mesh, MultiQuad, Node, NormalShader, OrbitControls, OrthographicCamera, PerspectiveCamera, PhongShader, Plane, PointLight, Program, Quad, QuadBatch, RenderTarget, Renderer, Scene, Shader, Sphere, SpotLight, Sprite, SpriteShader, SubMesh, Texture, Uniform, VWing, eDrawMethod };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@1pizzateam/roostr",
3
- "version": "0.8.1",
3
+ "version": "0.9.0",
4
4
  "description": "WebGL 3D engine written in TypeScript",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -62,6 +62,6 @@
62
62
  "typescript": "6.0.3"
63
63
  },
64
64
  "dependencies": {
65
- "@1pizzateam/spock": "4.4.0"
65
+ "@1pizzateam/spock": "4.5.0"
66
66
  }
67
67
  }