dacha 0.18.0-alpha.9 → 0.18.1

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.
Files changed (66) hide show
  1. package/build/contrib/components/bitmap-text/index.d.ts +11 -11
  2. package/build/contrib/components/bitmap-text/index.js +13 -10
  3. package/build/contrib/components/index.d.ts +1 -0
  4. package/build/contrib/components/index.js +1 -0
  5. package/build/contrib/components/interpolation/index.d.ts +85 -0
  6. package/build/contrib/components/interpolation/index.js +101 -0
  7. package/build/contrib/components/mesh/index.d.ts +13 -13
  8. package/build/contrib/components/mesh/index.js +12 -9
  9. package/build/contrib/components/pixi-view/index.d.ts +5 -5
  10. package/build/contrib/components/pixi-view/index.js +5 -2
  11. package/build/contrib/components/rigid-body/index.d.ts +8 -0
  12. package/build/contrib/components/rigid-body/index.js +8 -0
  13. package/build/contrib/components/shape/index.d.ts +20 -36
  14. package/build/contrib/components/shape/index.js +27 -23
  15. package/build/contrib/components/sprite/index.d.ts +22 -14
  16. package/build/contrib/components/sprite/index.js +23 -10
  17. package/build/contrib/systems/index.d.ts +2 -0
  18. package/build/contrib/systems/index.js +1 -0
  19. package/build/contrib/systems/interpolator/api.d.ts +40 -0
  20. package/build/contrib/systems/interpolator/api.js +70 -0
  21. package/build/contrib/systems/interpolator/index.d.ts +3 -0
  22. package/build/contrib/systems/interpolator/index.js +2 -0
  23. package/build/contrib/systems/interpolator/system.d.ts +31 -0
  24. package/build/contrib/systems/interpolator/system.js +96 -0
  25. package/build/contrib/systems/interpolator/tests/helpers.d.ts +13 -0
  26. package/build/contrib/systems/interpolator/tests/helpers.js +45 -0
  27. package/build/contrib/systems/interpolator/utils.d.ts +5 -0
  28. package/build/contrib/systems/interpolator/utils.js +41 -0
  29. package/build/contrib/systems/physics-system/api.d.ts +45 -1
  30. package/build/contrib/systems/physics-system/api.js +49 -0
  31. package/build/contrib/systems/physics-system/consts.d.ts +3 -1
  32. package/build/contrib/systems/physics-system/consts.js +4 -1
  33. package/build/contrib/systems/physics-system/physics-system.js +7 -1
  34. package/build/contrib/systems/physics-system/subsystems/collision-detection/dynamic-aabb-tree/index.d.ts +1 -1
  35. package/build/contrib/systems/physics-system/subsystems/collision-detection/dynamic-aabb-tree/index.js +21 -15
  36. package/build/contrib/systems/physics-system/subsystems/collision-detection/index.d.ts +11 -11
  37. package/build/contrib/systems/physics-system/subsystems/collision-detection/index.js +130 -124
  38. package/build/contrib/systems/physics-system/subsystems/collision-detection/query-utils.d.ts +5 -0
  39. package/build/contrib/systems/physics-system/subsystems/collision-detection/query-utils.js +97 -81
  40. package/build/contrib/systems/physics-system/subsystems/collision-detection/types.d.ts +0 -15
  41. package/build/contrib/systems/physics-system/subsystems/collision-detection/utils.d.ts +3 -0
  42. package/build/contrib/systems/physics-system/subsystems/collision-detection/utils.js +9 -0
  43. package/build/contrib/systems/physics-system/subsystems/physics/mass-properties.js +1 -1
  44. package/build/contrib/systems/physics-system/types.d.ts +12 -2
  45. package/build/contrib/systems/renderer/actor-render-tree.js +15 -6
  46. package/build/contrib/systems/renderer/builders/mesh-builder/index.d.ts +0 -1
  47. package/build/contrib/systems/renderer/builders/mesh-builder/index.js +0 -21
  48. package/build/contrib/systems/renderer/builders/pixi-view-builder/index.js +2 -1
  49. package/build/contrib/systems/renderer/builders/sprite-builder/index.js +8 -0
  50. package/build/contrib/systems/renderer/material/consts.d.ts +1 -1
  51. package/build/contrib/systems/renderer/material/consts.js +3 -1
  52. package/build/contrib/systems/renderer/material/index.js +11 -5
  53. package/build/engine/data-lib/index.d.ts +1 -1
  54. package/build/engine/data-lib/index.js +1 -1
  55. package/build/engine/data-lib/pool.d.ts +9 -0
  56. package/build/engine/data-lib/pool.js +19 -0
  57. package/build/engine/game-loop.js +3 -3
  58. package/build/engine/math-lib/math/ops.d.ts +16 -0
  59. package/build/engine/math-lib/math/ops.js +25 -0
  60. package/package.json +1 -1
  61. package/build/contrib/systems/physics-system/subsystems/collision-detection/dispersion-calculator/index.d.ts +0 -11
  62. package/build/contrib/systems/physics-system/subsystems/collision-detection/dispersion-calculator/index.js +0 -41
  63. package/build/engine/data-lib/sort/index.d.ts +0 -1
  64. package/build/engine/data-lib/sort/index.js +0 -1
  65. package/build/engine/data-lib/sort/insertion-sort.d.ts +0 -1
  66. package/build/engine/data-lib/sort/insertion-sort.js +0 -14
@@ -1,3 +1,4 @@
1
+ import { Vector2 } from '../../../../../engine/math-lib';
1
2
  import { Collider, Transform } from '../../../../components';
2
3
  import { geometryBuilders } from './geometry-builders';
3
4
  import { aabbBuilders } from './aabb-builders';
@@ -10,7 +11,7 @@ export function buildQueryProxy(type, params) {
10
11
  aabb: aabbBuilders[type](geometry),
11
12
  geometry,
12
13
  layer: params.layer,
13
- excludedActors: params.excludeActors
14
+ excludedActors: params.excludeActors?.length
14
15
  ? new Set(params.excludeActors)
15
16
  : undefined,
16
17
  actorFilter: params.actorFilter,
@@ -62,130 +63,145 @@ export const getActorCastQueryType = (params) => {
62
63
  return null;
63
64
  }
64
65
  };
66
+ export const makeCastHit = () => ({
67
+ actor: undefined,
68
+ point: { x: 0, y: 0 },
69
+ normal: new Vector2(0, 0),
70
+ distance: 0,
71
+ });
72
+ const cloneCastHit = (hit) => ({
73
+ actor: hit.actor,
74
+ point: hit.point,
75
+ normal: hit.normal,
76
+ distance: hit.distance,
77
+ });
78
+ export const makeOverlapHit = () => ({
79
+ actor: undefined,
80
+ normal: new Vector2(0, 0),
81
+ penetration: 0,
82
+ contactPoints: [],
83
+ });
84
+ const cloneOverlapHit = (hit) => ({
85
+ actor: hit.actor,
86
+ normal: hit.normal,
87
+ penetration: hit.penetration,
88
+ contactPoints: hit.contactPoints,
89
+ });
65
90
  export const overlap = (type, queryProxy, proxies) => {
91
+ const scratch = makeOverlapHit();
66
92
  const hits = [];
67
- for (const proxy of proxies) {
68
- const collider = proxy.actor.getComponent(Collider);
69
- const checker = intersectionCheckers[type]?.[collider.shape.type];
70
- if (!checker) {
71
- continue;
72
- }
73
- const intersection = checker(queryProxy.geometry, proxy.geometry);
74
- if (!intersection) {
75
- continue;
76
- }
77
- const hit = {
78
- actor: proxy.actor,
79
- normal: intersection.normal.clone().multiplyNumber(-1),
80
- penetration: intersection.penetration,
81
- contactPoints: intersection.contactPoints,
82
- };
83
- if (queryProxy.hitFilter && !queryProxy.hitFilter(hit)) {
84
- continue;
85
- }
86
- hits.push(hit);
87
- }
93
+ overlapEach(type, queryProxy, proxies, scratch, (hit) => {
94
+ hits.push(cloneOverlapHit(hit));
95
+ });
88
96
  return hits;
89
97
  };
90
98
  export const raycast = (queryProxy, proxies) => {
99
+ const scratch = makeCastHit();
91
100
  let nearestDistance = Infinity;
92
101
  let nearestHit = null;
93
- for (const proxy of proxies) {
94
- const collider = proxy.actor.getComponent(Collider);
95
- const hit = raycastCheckers.ray[collider.shape.type](queryProxy.geometry, proxy.geometry);
96
- if (!hit) {
97
- continue;
98
- }
99
- const castHit = {
100
- actor: proxy.actor,
101
- point: hit.point,
102
- normal: hit.normal,
103
- distance: hit.distance,
104
- };
105
- if (queryProxy.hitFilter && !queryProxy.hitFilter(castHit)) {
106
- continue;
107
- }
102
+ raycastEach(queryProxy, proxies, scratch, (hit) => {
108
103
  if (hit.distance < nearestDistance) {
109
- nearestHit = castHit;
110
104
  nearestDistance = hit.distance;
105
+ nearestHit = nearestHit ?? makeCastHit();
106
+ nearestHit.actor = hit.actor;
107
+ nearestHit.point = hit.point;
108
+ nearestHit.normal = hit.normal;
109
+ nearestHit.distance = hit.distance;
111
110
  }
112
- }
111
+ });
113
112
  return nearestHit;
114
113
  };
115
114
  export const raycastAll = (queryProxy, proxies) => {
115
+ const scratch = makeCastHit();
116
116
  const hits = [];
117
+ raycastEach(queryProxy, proxies, scratch, (hit) => {
118
+ hits.push(cloneCastHit(hit));
119
+ });
120
+ hits.sort((arg1, arg2) => arg1.distance - arg2.distance);
121
+ return hits;
122
+ };
123
+ export const shapeCast = (type, queryProxy, proxies) => {
124
+ const scratch = makeCastHit();
125
+ let nearestDistance = Infinity;
126
+ let nearestHit = null;
127
+ shapeCastEach(type, queryProxy, proxies, scratch, (hit) => {
128
+ if (hit.distance < nearestDistance) {
129
+ nearestDistance = hit.distance;
130
+ nearestHit = nearestHit ?? makeCastHit();
131
+ nearestHit.actor = hit.actor;
132
+ nearestHit.point = hit.point;
133
+ nearestHit.normal = hit.normal;
134
+ nearestHit.distance = hit.distance;
135
+ }
136
+ });
137
+ return nearestHit;
138
+ };
139
+ export const shapeCastAll = (type, queryProxy, proxies) => {
140
+ const scratch = makeCastHit();
141
+ const hits = [];
142
+ shapeCastEach(type, queryProxy, proxies, scratch, (hit) => {
143
+ hits.push(cloneCastHit(hit));
144
+ });
145
+ hits.sort((arg1, arg2) => arg1.distance - arg2.distance);
146
+ return hits;
147
+ };
148
+ export const raycastEach = (queryProxy, proxies, hit, callback) => {
117
149
  for (const proxy of proxies) {
118
150
  const collider = proxy.actor.getComponent(Collider);
119
- const hit = raycastCheckers.ray[collider.shape.type](queryProxy.geometry, proxy.geometry);
120
- if (!hit) {
151
+ const result = raycastCheckers.ray[collider.shape.type](queryProxy.geometry, proxy.geometry);
152
+ if (!result) {
121
153
  continue;
122
154
  }
123
- const castHit = {
124
- actor: proxy.actor,
125
- point: hit.point,
126
- normal: hit.normal,
127
- distance: hit.distance,
128
- };
129
- if (queryProxy.hitFilter && !queryProxy.hitFilter(castHit)) {
155
+ hit.actor = proxy.actor;
156
+ hit.point = result.point;
157
+ hit.normal = result.normal;
158
+ hit.distance = result.distance;
159
+ if (queryProxy.hitFilter && !queryProxy.hitFilter(hit)) {
130
160
  continue;
131
161
  }
132
- hits.push(castHit);
162
+ callback(hit);
133
163
  }
134
- hits.sort((arg1, arg2) => arg1.distance - arg2.distance);
135
- return hits;
136
164
  };
137
- export const shapeCast = (type, queryProxy, proxies) => {
138
- let nearestDistance = Infinity;
139
- let nearestHit = null;
165
+ export const shapeCastEach = (type, queryProxy, proxies, hit, callback) => {
140
166
  for (const proxy of proxies) {
141
167
  const collider = proxy.actor.getComponent(Collider);
142
168
  const checker = shapeCastCheckers[type]?.[collider.shape.type];
143
169
  if (!checker) {
144
170
  continue;
145
171
  }
146
- const castHit = checker(queryProxy.geometry, proxy.geometry);
147
- if (!castHit) {
172
+ const result = checker(queryProxy.geometry, proxy.geometry);
173
+ if (!result) {
148
174
  continue;
149
175
  }
150
- const hit = {
151
- actor: proxy.actor,
152
- point: castHit.point,
153
- normal: castHit.normal,
154
- distance: castHit.distance,
155
- };
176
+ hit.actor = proxy.actor;
177
+ hit.point = result.point;
178
+ hit.normal = result.normal;
179
+ hit.distance = result.distance;
156
180
  if (queryProxy.hitFilter && !queryProxy.hitFilter(hit)) {
157
181
  continue;
158
182
  }
159
- if (castHit.distance < nearestDistance) {
160
- nearestHit = hit;
161
- nearestDistance = castHit.distance;
162
- }
183
+ callback(hit);
163
184
  }
164
- return nearestHit;
165
185
  };
166
- export const shapeCastAll = (type, queryProxy, proxies) => {
167
- const hits = [];
186
+ export const overlapEach = (type, queryProxy, proxies, hit, callback) => {
168
187
  for (const proxy of proxies) {
169
188
  const collider = proxy.actor.getComponent(Collider);
170
- const checker = shapeCastCheckers[type]?.[collider.shape.type];
189
+ const checker = intersectionCheckers[type]?.[collider.shape.type];
171
190
  if (!checker) {
172
191
  continue;
173
192
  }
174
- const castHit = checker(queryProxy.geometry, proxy.geometry);
175
- if (!castHit) {
193
+ const intersection = checker(queryProxy.geometry, proxy.geometry);
194
+ if (!intersection) {
176
195
  continue;
177
196
  }
178
- const hit = {
179
- actor: proxy.actor,
180
- point: castHit.point,
181
- normal: castHit.normal,
182
- distance: castHit.distance,
183
- };
197
+ intersection.normal.multiplyNumber(-1);
198
+ hit.actor = proxy.actor;
199
+ hit.normal = intersection.normal;
200
+ hit.penetration = intersection.penetration;
201
+ hit.contactPoints = intersection.contactPoints;
184
202
  if (queryProxy.hitFilter && !queryProxy.hitFilter(hit)) {
185
203
  continue;
186
204
  }
187
- hits.push(hit);
205
+ callback(hit);
188
206
  }
189
- hits.sort((arg1, arg2) => arg1.distance - arg2.distance);
190
- return hits;
191
207
  };
@@ -2,7 +2,6 @@ import type { Vector2, Point } from '../../../../../engine/math-lib';
2
2
  import type { Actor } from '../../../../../engine/actor';
3
3
  import type { CastHit, OverlapHit } from '../../types';
4
4
  import type { DynamicAABBTreeEntryId } from './dynamic-aabb-tree';
5
- import type { DispersionCalculator } from './dispersion-calculator';
6
5
  export type { Point };
7
6
  export interface AABB {
8
7
  min: Point;
@@ -83,7 +82,6 @@ export interface ActorProxy {
83
82
  aabb: AABB;
84
83
  geometry: Geometry;
85
84
  orientationData: OrientationData;
86
- edges: Record<Axis, [SortedItem, SortedItem]>;
87
85
  treeEntryId: DynamicAABBTreeEntryId;
88
86
  layer: string;
89
87
  }
@@ -96,19 +94,6 @@ export interface QueryProxy {
96
94
  hitFilter?: (hit: OverlapHit | CastHit) => boolean;
97
95
  }
98
96
  export type Proxy = ActorProxy | QueryProxy;
99
- export interface SortedItem {
100
- proxy: ActorProxy;
101
- value: number;
102
- }
103
- export interface AxisEntry {
104
- sortedList: SortedItem[];
105
- dispersionCalculator: DispersionCalculator;
106
- }
107
- export type Axis = 'x' | 'y';
108
- export interface Axes {
109
- x: AxisEntry;
110
- y: AxisEntry;
111
- }
112
97
  export type ProxyPair = [ActorProxy, ActorProxy];
113
98
  export interface Contact {
114
99
  actor1: Actor;
@@ -1,5 +1,8 @@
1
+ import type { ActorProxy } from './types';
1
2
  export declare const isZero: (value: number) => boolean;
2
3
  export declare const isDefinitelyPositive: (value: number) => boolean;
3
4
  export declare const isDefinitelyNegative: (value: number) => boolean;
4
5
  export declare const isGreaterThan: (left: number, right: number) => boolean;
5
6
  export declare const isLessThan: (left: number, right: number) => boolean;
7
+ export declare const isStatic: (proxy: ActorProxy) => boolean;
8
+ export declare const isDisabled: (proxy: ActorProxy) => boolean;
@@ -1,6 +1,15 @@
1
+ import { RigidBody, Collider } from '../../../../components';
1
2
  import { INTERSECTION_EPSILON } from './constants';
2
3
  export const isZero = (value) => Math.abs(value) <= INTERSECTION_EPSILON;
3
4
  export const isDefinitelyPositive = (value) => value > INTERSECTION_EPSILON;
4
5
  export const isDefinitelyNegative = (value) => value < -INTERSECTION_EPSILON;
5
6
  export const isGreaterThan = (left, right) => isDefinitelyPositive(left - right);
6
7
  export const isLessThan = (left, right) => isDefinitelyNegative(left - right);
8
+ export const isStatic = (proxy) => {
9
+ const rigidBody = proxy.actor.getComponent(RigidBody);
10
+ return rigidBody?.type === 'static';
11
+ };
12
+ export const isDisabled = (proxy) => {
13
+ const collider = proxy.actor.getComponent(Collider);
14
+ return collider?.disabled === true;
15
+ };
@@ -47,7 +47,7 @@ export const calculateInertia = (mass, collider, transform) => {
47
47
  if (length <= 0) {
48
48
  return 0;
49
49
  }
50
- inertia = (mass * (length * length + 1)) / 12;
50
+ inertia = (mass * length * length) / 12;
51
51
  break;
52
52
  }
53
53
  }
@@ -2,8 +2,8 @@ import type { SceneSystemOptions } from '../../../engine/system';
2
2
  import type { Actor } from '../../../engine/actor';
3
3
  import type { Vector2, Point } from '../../../engine/math-lib';
4
4
  export interface PhysicsSystemOptions extends SceneSystemOptions {
5
- gravityX: number;
6
- gravityY: number;
5
+ gravityX?: number;
6
+ gravityY?: number;
7
7
  solverIterations?: number;
8
8
  linearSleepThreshold?: number;
9
9
  angularSleepThreshold?: number;
@@ -24,6 +24,14 @@ export interface PhysicsQueryFilter<T> {
24
24
  layer?: string;
25
25
  excludeActors?: Actor[];
26
26
  actorFilter?: (actor: Actor) => boolean;
27
+ /**
28
+ * Called once per candidate hit to decide whether to keep it.
29
+ *
30
+ * The `hit` object passed to this callback may be reused and is only valid
31
+ * for the duration of the call.
32
+ * Inspect it and return a boolean; do not retain the reference or store it
33
+ * in an external collection.
34
+ */
27
35
  hitFilter?: (hit: T) => boolean;
28
36
  }
29
37
  export interface CommonCastParams extends PhysicsQueryFilter<CastHit> {
@@ -45,6 +53,8 @@ export interface OverlapHit {
45
53
  penetration: number;
46
54
  contactPoints: Point[];
47
55
  }
56
+ export type CastHitCallback = (hit: CastHit) => void;
57
+ export type OverlapHitCallback = (hit: OverlapHit) => void;
48
58
  export interface PointQueryShape {
49
59
  type: 'point';
50
60
  point: Point;
@@ -1,6 +1,7 @@
1
1
  import { Container } from 'pixi.js';
2
2
  import { Actor } from '../../../engine/actor';
3
3
  import { Transform } from '../../components/transform';
4
+ import { Interpolation } from '../../components/interpolation';
4
5
  import { Sprite } from '../../components/sprite';
5
6
  import { Shape } from '../../components/shape';
6
7
  import { PixiView } from '../../components/pixi-view';
@@ -146,17 +147,25 @@ export class ActorRenderTree {
146
147
  this.actorParentMap.set(actor, actor.parent);
147
148
  }
148
149
  updatePosition(container, actor) {
149
- const { local: { position, rotation, scale }, } = actor.getComponent(Transform);
150
+ const { local } = actor.getComponent(Transform);
151
+ const interpolation = actor.getComponent(Interpolation);
152
+ const useRender = interpolation !== undefined &&
153
+ !interpolation.disabled &&
154
+ interpolation.initialized;
155
+ const positionX = useRender ? interpolation.renderX : local.position.x;
156
+ const positionY = useRender ? interpolation.renderY : local.position.y;
157
+ const rotation = useRender ? interpolation.renderRotation : local.rotation;
158
+ const { scale } = local;
150
159
  const meta = container.__dacha.meta;
151
160
  if (rotation !== meta.rotation) {
152
161
  container.rotation = rotation;
153
162
  meta.rotation = rotation;
154
163
  }
155
- if (!floatEquals(position.x, meta.positionX) ||
156
- !floatEquals(position.y, meta.positionY)) {
157
- container.position.set(position.x, position.y);
158
- meta.positionX = position.x;
159
- meta.positionY = position.y;
164
+ if (!floatEquals(positionX, meta.positionX) ||
165
+ !floatEquals(positionY, meta.positionY)) {
166
+ container.position.set(positionX, positionY);
167
+ meta.positionX = positionX;
168
+ meta.positionY = positionY;
160
169
  }
161
170
  if (scale.x !== meta.scaleX || scale.y !== meta.scaleY) {
162
171
  container.scale.set(scale.x, scale.y);
@@ -16,7 +16,6 @@ export declare class MeshBuilder implements Builder<Mesh> {
16
16
  destroy(mesh: Mesh): void;
17
17
  buildView(mesh: Mesh, actor: Actor): PixiMesh;
18
18
  updateView(mesh: Mesh): void;
19
- private updateGeometry;
20
19
  private updateTexture;
21
20
  }
22
21
  export {};
@@ -75,26 +75,6 @@ export class MeshBuilder {
75
75
  }
76
76
  this.materialSystem.updateShader(mesh);
77
77
  }
78
- updateGeometry(mesh) {
79
- const view = mesh.renderData.view;
80
- const texture = view.texture;
81
- const geometry = view.geometry;
82
- const uvs = texture?.uvs;
83
- if (!uvs) {
84
- return;
85
- }
86
- const uvAttr = geometry.getAttribute('aUV');
87
- const uvBuffer = uvAttr.buffer.data;
88
- uvBuffer[0] = uvs.x0;
89
- uvBuffer[1] = uvs.y0;
90
- uvBuffer[2] = uvs.x1;
91
- uvBuffer[3] = uvs.y1;
92
- uvBuffer[4] = uvs.x2;
93
- uvBuffer[5] = uvs.y2;
94
- uvBuffer[6] = uvs.x3;
95
- uvBuffer[7] = uvs.y3;
96
- uvAttr.buffer.update();
97
- }
98
78
  updateTexture(mesh) {
99
79
  const view = mesh.renderData.view;
100
80
  const meta = view.__dacha.meta;
@@ -109,6 +89,5 @@ export class MeshBuilder {
109
89
  const texture = textureArray?.[mesh.currentFrame];
110
90
  view.texture = texture ?? Texture.WHITE;
111
91
  meta.currentFrame = mesh.currentFrame;
112
- this.updateGeometry(mesh);
113
92
  }
114
93
  }
@@ -1,3 +1,4 @@
1
+ import { Graphics } from 'pixi.js';
1
2
  import { PixiView } from '../../../../components/pixi-view';
2
3
  export class PixiViewBuilder {
3
4
  destroy(pixiView) {
@@ -5,7 +6,7 @@ export class PixiViewBuilder {
5
6
  pixiView.renderData = undefined;
6
7
  }
7
8
  buildView(pixiView, actor) {
8
- const view = pixiView.createView();
9
+ const view = pixiView.createView ? pixiView.createView() : new Graphics();
9
10
  pixiView.renderData = { view };
10
11
  view.__dacha = {
11
12
  actor,
@@ -78,5 +78,13 @@ export class SpriteBuilder {
78
78
  meta.width = sprite.width;
79
79
  meta.height = sprite.height;
80
80
  }
81
+ if (sprite.fit === 'repeat') {
82
+ if (sprite.textureOffset.x !== meta.textureOffsetX ||
83
+ sprite.textureOffset.y !== meta.textureOffsetY) {
84
+ view.tilePosition.set(sprite.textureOffset.x, sprite.textureOffset.y);
85
+ meta.textureOffsetX = sprite.textureOffset.x;
86
+ meta.textureOffsetY = sprite.textureOffset.y;
87
+ }
88
+ }
81
89
  }
82
90
  }
@@ -1,2 +1,2 @@
1
1
  export declare const DEFAULT_VERTEX_SHADER = "\n precision mediump float;\n\n attribute vec2 aPosition;\n attribute vec2 aUV;\n\n uniform mat3 uProjectionMatrix;\n uniform mat3 uWorldTransformMatrix;\n uniform mat3 uTransformMatrix;\n\n varying vec2 vUV;\n\n void main() {\n vUV = aUV;\n\n mat3 mvp = uProjectionMatrix * uWorldTransformMatrix * uTransformMatrix;\n vec3 pos = mvp * vec3(aPosition, 1.0);\n gl_Position = vec4(pos.xy, 0.0, 1.0);\n }\n";
2
- export declare const DEFAULT_FRAGMENT_SHADER = "\n precision mediump float;\n\n varying vec2 vUV;\n\n uniform sampler2D uSampler;\n uniform vec3 uTint;\n uniform float uAlpha;\n\n void main() {\n vec4 color = texture2D(uSampler, vUV);\n color.rgb *= uTint;\n color *= uAlpha;\n gl_FragColor = color;\n }\n";
2
+ export declare const DEFAULT_FRAGMENT_SHADER = "\n precision mediump float;\n\n varying vec2 vUV;\n\n uniform sampler2D uSampler;\n uniform vec2 uUVOffset;\n uniform vec2 uUVScale;\n uniform vec3 uTint;\n uniform float uAlpha;\n\n void main() {\n vec4 color = texture2D(uSampler, uUVOffset + vUV * uUVScale);\n color.rgb *= uTint;\n color *= uAlpha;\n gl_FragColor = color;\n }\n";
@@ -24,11 +24,13 @@ export const DEFAULT_FRAGMENT_SHADER = `
24
24
  varying vec2 vUV;
25
25
 
26
26
  uniform sampler2D uSampler;
27
+ uniform vec2 uUVOffset;
28
+ uniform vec2 uUVScale;
27
29
  uniform vec3 uTint;
28
30
  uniform float uAlpha;
29
31
 
30
32
  void main() {
31
- vec4 color = texture2D(uSampler, vUV);
33
+ vec4 color = texture2D(uSampler, uUVOffset + vUV * uUVScale);
32
34
  color.rgb *= uTint;
33
35
  color *= uAlpha;
34
36
  gl_FragColor = color;
@@ -35,7 +35,9 @@ export class MaterialSystem {
35
35
  uTime: { value: 0.0, type: 'f32' },
36
36
  uTint: { value: [1.0, 1.0, 1.0], type: 'vec3<f32>' },
37
37
  uAlpha: { value: 1.0, type: 'f32' },
38
- uTextureSize: { value: [0.0, 0.0], type: 'vec2<f32>' },
38
+ uFrameSize: { value: [0.0, 0.0], type: 'vec2<f32>' },
39
+ uUVOffset: { value: [0.0, 0.0], type: 'vec2<f32>' },
40
+ uUVScale: { value: [1.0, 1.0], type: 'vec2<f32>' },
39
41
  ...(config && builder?.uniforms?.(config.options)),
40
42
  },
41
43
  },
@@ -78,10 +80,14 @@ export class MaterialSystem {
78
80
  .setValue(tint)
79
81
  .toRgbArray([]);
80
82
  }
81
- view.shader.resources.uniformsGroup.uniforms.uTextureSize[0] =
82
- view.texture.source.width;
83
- view.shader.resources.uniformsGroup.uniforms.uTextureSize[1] =
84
- view.texture.source.height;
83
+ if (meta.materialTexture !== view.texture) {
84
+ meta.materialTexture = view.texture;
85
+ const { uvs, frame } = view.texture;
86
+ const uniforms = view.shader.resources.uniformsGroup.uniforms;
87
+ uniforms.uUVOffset = [uvs.x0, uvs.y0];
88
+ uniforms.uUVScale = [uvs.x1 - uvs.x0, uvs.y2 - uvs.y0];
89
+ uniforms.uFrameSize = [frame.width, frame.height];
90
+ }
85
91
  if (!material || !builder) {
86
92
  return;
87
93
  }
@@ -1,3 +1,3 @@
1
1
  export { Queue } from './queue';
2
2
  export { CacheStore } from './cache-store';
3
- export { insertionSort } from './sort';
3
+ export { Pool } from './pool';
@@ -1,3 +1,3 @@
1
1
  export { Queue } from './queue';
2
2
  export { CacheStore } from './cache-store';
3
- export { insertionSort } from './sort';
3
+ export { Pool } from './pool';
@@ -0,0 +1,9 @@
1
+ export declare class Pool<T> {
2
+ private factory;
3
+ private reset?;
4
+ private items;
5
+ constructor(factory: () => T, reset?: ((item: T) => void) | undefined);
6
+ acquire(): T;
7
+ release(item: T): void;
8
+ clear(): void;
9
+ }
@@ -0,0 +1,19 @@
1
+ export class Pool {
2
+ factory;
3
+ reset;
4
+ items = [];
5
+ constructor(factory, reset) {
6
+ this.factory = factory;
7
+ this.reset = reset;
8
+ }
9
+ acquire() {
10
+ return this.items.pop() ?? this.factory();
11
+ }
12
+ release(item) {
13
+ this.reset?.(item);
14
+ this.items.push(item);
15
+ }
16
+ clear() {
17
+ this.items.length = 0;
18
+ }
19
+ }
@@ -27,10 +27,10 @@ export class GameLoop {
27
27
  this.lag = 0;
28
28
  this.bindedTick = this.tick.bind(this);
29
29
  }
30
- tick() {
30
+ tick(timestamp) {
31
31
  this.gameLoopId = requestAnimationFrame(this.bindedTick);
32
- const current = performance.now();
33
- const elapsed = Math.min(current - this.previous, this.maxFrameDelta);
32
+ const current = timestamp;
33
+ const elapsed = Math.min(Math.max(current - this.previous, 0), this.maxFrameDelta);
34
34
  if (elapsed < this.msPerUpdate) {
35
35
  return;
36
36
  }
@@ -118,4 +118,20 @@ export declare class MathOps {
118
118
  * ```
119
119
  */
120
120
  static clamp(value: number, min: number, max: number): number;
121
+ /**
122
+ * Signed shortest angular difference between two angles in radians
123
+ *
124
+ * The result is in (-PI, PI], so `from + delta` is angle-equivalent
125
+ * to `to` while rotating the short way around.
126
+ *
127
+ * @param from - Start angle in radians
128
+ * @param to - End angle in radians
129
+ * @returns Signed delta in radians
130
+ *
131
+ * @example
132
+ * ```typescript
133
+ * const delta = MathOps.getAngleDelta(3, -3); // ~0.283, crosses PI
134
+ * ```
135
+ */
136
+ static getAngleDelta(from: number, to: number): number;
121
137
  }
@@ -134,4 +134,29 @@ export class MathOps {
134
134
  }
135
135
  return value;
136
136
  }
137
+ /**
138
+ * Signed shortest angular difference between two angles in radians
139
+ *
140
+ * The result is in (-PI, PI], so `from + delta` is angle-equivalent
141
+ * to `to` while rotating the short way around.
142
+ *
143
+ * @param from - Start angle in radians
144
+ * @param to - End angle in radians
145
+ * @returns Signed delta in radians
146
+ *
147
+ * @example
148
+ * ```typescript
149
+ * const delta = MathOps.getAngleDelta(3, -3); // ~0.283, crosses PI
150
+ * ```
151
+ */
152
+ static getAngleDelta(from, to) {
153
+ let delta = (to - from) % (2 * Math.PI);
154
+ if (delta > Math.PI) {
155
+ delta -= 2 * Math.PI;
156
+ }
157
+ else if (delta <= -Math.PI) {
158
+ delta += 2 * Math.PI;
159
+ }
160
+ return delta;
161
+ }
137
162
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dacha",
3
- "version": "0.18.0-alpha.9",
3
+ "version": "0.18.1",
4
4
  "main": "./build/index.js",
5
5
  "types": "./build/index.d.ts",
6
6
  "exports": {
@@ -1,11 +0,0 @@
1
- import type { Axis, AABB } from '../types';
2
- export declare class DispersionCalculator {
3
- private axis;
4
- private sampleSize;
5
- private sum;
6
- private squaredSum;
7
- constructor(axis: Axis);
8
- addToSample(aabb: AABB): void;
9
- removeFromSample(aabb: AABB): void;
10
- getDispersion(): number;
11
- }