@woosh/meep-engine 2.42.8 → 2.43.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/core/binary/BitSet.js +4 -4
- package/core/binary/ctz32.js +40 -0
- package/core/collection/ObservedMap.js +61 -57
- package/core/collection/heap/FastBinaryHeap.js +7 -1
- package/core/collection/heap/Uint32Heap.js +19 -0
- package/core/collection/map/AsyncLoadingCache.js +3 -1
- package/core/geom/2d/compute_polygon_area_2d.js +32 -0
- package/core/geom/2d/compute_polygon_area_2d.spec.js +10 -0
- package/core/geom/2d/compute_triangle_area_2d.js +15 -0
- package/core/geom/2d/compute_triangle_area_2d.spec.js +9 -0
- package/core/geom/2d/convex-hull/convex_hull_jarvis_2d.js +64 -0
- package/core/geom/2d/convex-hull/convex_hull_jarvis_2d.spec.js +33 -0
- package/core/geom/2d/convex-hull/convex_hull_monotone_2d.js +82 -0
- package/core/geom/2d/convex-hull/fixed_convex_hull_humus.js +135 -0
- package/core/geom/2d/convex-hull/fixed_convex_hull_relaxation.js +282 -0
- package/core/geom/2d/convex-hull/orientation3.js +444 -0
- package/core/geom/2d/convex-hull/orientation3_array.js +22 -0
- package/core/geom/2d/convex-hull/orientation3_v2.js +12 -0
- package/core/geom/2d/intersect_ray_2d.js +56 -0
- package/core/geom/2d/quad-tree/QuadTreeNode.js +0 -81
- package/core/geom/2d/quad-tree/qt_match_data_by_circle.js +70 -0
- package/core/geom/3d/matrix/m4_multiply_alphatensor.js +131 -0
- package/core/geom/3d/plane/orient3d_fast.js +2 -6
- package/core/geom/3d/tetrahedra/README.md +7 -0
- package/core/geom/3d/tetrahedra/compute_bounding_simplex_3d.js +3 -1
- package/core/geom/3d/tetrahedra/delaunay/Cavity.js +48 -0
- package/core/geom/3d/tetrahedra/{compute_delaunay_tetrahedral_mesh.js → delaunay/compute_delaunay_tetrahedral_mesh.js} +15 -7
- package/core/geom/3d/tetrahedra/{compute_delaunay_tetrahedral_mesh.spec.js → delaunay/compute_delaunay_tetrahedral_mesh.spec.js} +0 -0
- package/core/geom/3d/tetrahedra/delaunay/tetrahedral_mesh_compute_cavity.js +73 -0
- package/core/geom/3d/tetrahedra/delaunay/tetrahedral_mesh_walk_toward_cavity.js +48 -0
- package/core/geom/3d/tetrahedra/hxt/a.js +524 -0
- package/core/geom/3d/tetrahedra/hxt/hxt.js +140 -0
- package/core/geom/3d/tetrahedra/hxt/hxt.wasm +0 -0
- package/core/geom/3d/tetrahedra/point_in_tetrahedron_circumsphere.js +35 -20
- package/core/geom/3d/tetrahedra/prototypeTetrahedraBuilder.js +98 -0
- package/core/geom/3d/tetrahedra/tetrahedra_collection.js +60 -131
- package/core/geom/packing/{MaxRectangles.js → max-rect/MaxRectangles.js} +28 -124
- package/core/geom/packing/max-rect/removeRedundantBoxes.js +69 -0
- package/core/geom/packing/max-rect/removeRedundantBoxesArray.js +40 -0
- package/core/geom/v3_distance_above_plane.js +1 -1
- package/core/graph/layout/BoxLayouter.js +2 -88
- package/core/graph/layout/CircleLayout.js +2 -1
- package/core/graph/layout/box/forceIntoBox.js +45 -0
- package/core/graph/layout/box/pullBoxTowardsPoint.js +20 -0
- package/core/graph/layout/box/resolveAABB2Overlap.js +22 -0
- package/core/math/bessel_3.js +11 -0
- package/core/math/bessel_i0.js +26 -0
- package/core/process/executor/ConcurrentExecutor.spec.js +2 -1
- package/core/process/task/util/actionTask.js +19 -0
- package/core/process/task/util/countTask.js +62 -0
- package/core/process/task/util/delayTask.js +45 -0
- package/core/process/task/util/emptyTask.js +19 -0
- package/core/process/task/util/failingTask.js +17 -0
- package/core/process/task/util/futureTask.js +48 -0
- package/core/process/task/util/promiseTask.js +42 -0
- package/core/process/task/util/randomCountTask.js +64 -0
- package/core/process/task/util/wrapTaskIgnoreFailure.js +47 -0
- package/engine/Engine.js +8 -8
- package/engine/EngineBootstrapper.js +1 -1
- package/engine/asset/AssetManager.js +197 -53
- package/engine/asset/AssetRequest.js +32 -0
- package/engine/asset/loaders/ArrayBufferLoader.js +62 -50
- package/engine/asset/loaders/image/png/PNG.js +15 -1
- package/engine/asset/loaders/image/png/PNGReader.js +3 -2
- package/engine/ecs/foliage/ecs/InstancedMeshUtils.js +2 -1
- package/engine/ecs/storage/BinaryBufferDeSerializer.js +1 -1
- package/engine/ecs/storage/JSONDeSerializer.js +2 -1
- package/engine/ecs/terrain/ecs/splat/SplatMapOptimizer.js +2 -1
- package/engine/ecs/terrain/ecs/splat/SplatMapping.js +1 -1
- package/engine/graphics/camera/makeScreenScissorFrustum.js +1 -1
- package/engine/graphics/camera/testClippingPlaneComputation.js +4 -45
- package/engine/graphics/ecs/camera/FrustumProjector.js +6 -0
- package/engine/graphics/ecs/decal/v2/FPDecalSystem.js +5 -0
- package/engine/graphics/ecs/decal/v2/prototypeDecalSystem.js +23 -4
- package/engine/graphics/ecs/highlight/plugin/OutlineRenderPlugin.js +1 -1
- package/engine/graphics/ecs/mesh-v2/ShadedGeometry.js +11 -0
- package/engine/graphics/geometry/FULL_SCREEN_TRIANGLE_GEOMETRY.js +1 -2
- package/engine/graphics/impostors/octahedral/ImpostorBaker.js +5 -2
- package/engine/graphics/impostors/octahedral/ImpostorDescription.js +18 -0
- package/engine/graphics/impostors/octahedral/bake/prepare_bake_material.js +15 -0
- package/engine/graphics/impostors/octahedral/prototypeBaker.js +66 -79
- package/engine/graphics/impostors/octahedral/shader/ImpostorShaderWireframeV0.js +134 -0
- package/engine/graphics/impostors/octahedral/util/build_cutout_from_atlas_by_alpha.js +128 -0
- package/engine/graphics/impostors/octahedral/util/build_geometry_from_cutout_shape.js +32 -0
- package/engine/graphics/impostors/octahedral/util/load_mesh_for_bake.js +31 -0
- package/engine/graphics/impostors/octahedral/util/makeImpostorAtlasPreview.js +107 -0
- package/engine/graphics/material/manager/ManagedMaterial.js +4 -0
- package/engine/graphics/material/manager/MaterialManager.js +1 -0
- package/engine/graphics/material/optimization/MaterialOptimizationContext.js +7 -3
- package/engine/graphics/particles/particular/engine/renderers/billboard/ParticleBillboardMaterial.js +2 -2
- package/engine/graphics/render/forward_plus/prototype/prototypeLightManager.js +2 -2
- package/engine/graphics/render/visibility/hiz/buildCanvasViewFromTexture.js +83 -27
- package/engine/graphics/shadows/ShadowMapRenderer.js +11 -4
- package/engine/graphics/texture/atlas/AbstractTextureAtlas.js +2 -1
- package/engine/graphics/texture/atlas/CachingTextureAtlas.js +208 -38
- package/engine/graphics/texture/atlas/TextureAtlas.js +31 -24
- package/engine/graphics/texture/atlas/gpu/WebGLTextureAtlas.js +1 -1
- package/engine/graphics/texture/sampler/filter/box.js +16 -0
- package/engine/graphics/texture/sampler/filter/cubic2.js +32 -0
- package/engine/graphics/texture/sampler/filter/gaussian.js +16 -0
- package/engine/graphics/texture/sampler/filter/kaiser_1.js +19 -0
- package/engine/graphics/texture/sampler/filter/kaiser_bessel_window.js +19 -0
- package/engine/graphics/texture/sampler/filter/mitchell.js +55 -0
- package/engine/graphics/texture/sampler/filter/sampler2d_scale_down_generic.js +109 -0
- package/engine/graphics/texture/sampler/filter/triangle.js +19 -0
- package/engine/graphics/texture/sampler/prototypeSamplerFiltering.js +187 -86
- package/engine/graphics/texture/sampler/sampler2_d_scale_down_lanczos.js +77 -25
- package/engine/graphics/texture/sampler/search/make_edge_condition_channel_threshold.js +34 -0
- package/engine/graphics/texture/sampler/search/sampler2d_find_pixels.js +24 -0
- package/engine/graphics/texture/sprite/prototypeSpriteCutoutGeometry.js +212 -0
- package/engine/knowledge/database/StaticKnowledgeDataTable.js +1 -1
- package/engine/navigation/grid/AStar.js +1 -1
- package/engine/scene/Scene.js +1 -1
- package/engine/scene/SerializedScene.js +1 -1
- package/engine/scene/transitionToScene.js +3 -1
- package/generation/example/main.js +1 -1
- package/generation/grid/generation/GridTaskApplyActionToCells.js +1 -1
- package/generation/grid/generation/GridTaskDensityMarkerDistribution.js +1 -1
- package/generation/grid/generation/GridTaskExecuteRuleTimes.js +1 -1
- package/generation/grid/generation/NoopGridTaskGenerator.js +1 -1
- package/generation/grid/generation/discrete/GridTaskCellularAutomata.js +2 -1
- package/generation/grid/generation/discrete/GridTaskConnectRooms.js +1 -1
- package/generation/grid/generation/discrete/layer/GridTaskBuildSourceDistanceMap.js +3 -2
- package/generation/grid/generation/discrete/layer/GridTaskDistanceToMarkers.js +1 -1
- package/generation/grid/generation/grid/GridTaskAddNodesFixed.js +1 -1
- package/generation/grid/generation/road/GridTaskGenerateRoads.js +3 -2
- package/generation/grid/generation/util/buildDistanceMapToObjective.js +1 -1
- package/generation/markers/GridActionRuleSet.js +2 -1
- package/generation/placement/GridCellActionTransformNearbyMarkers.js +2 -4
- package/generation/theme/ThemeEngine.js +4 -1
- package/package.json +1 -1
- package/view/asset/AssetLoaderStatusView.js +5 -5
- package/view/minimap/gl/MinimapTerrainGL.js +1 -2
- package/view/renderModel.js +1 -1
- package/view/tooltip/TooltipView.js +5 -5
- package/core/process/task/TaskUtils.js +0 -352
|
@@ -1,13 +1,30 @@
|
|
|
1
|
-
import { Cache } from "../../../../core/cache/Cache.js";
|
|
2
1
|
import { AbstractTextureAtlas } from "./AbstractTextureAtlas.js";
|
|
3
|
-
|
|
2
|
+
|
|
3
|
+
class PatchRecord {
|
|
4
|
+
constructor() {
|
|
5
|
+
/**
|
|
6
|
+
*
|
|
7
|
+
* @type {AtlasPatch|null}
|
|
8
|
+
*/
|
|
9
|
+
this.patch = null;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* When was the patch last used
|
|
13
|
+
* @type {number}
|
|
14
|
+
*/
|
|
15
|
+
this.last_use_time = 0;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
4
18
|
|
|
5
19
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
20
|
+
* Adding and removing elements to atlas can be costly. It can trigger repack, it may require splatting or cleaning.
|
|
21
|
+
* This abstraction allows us to bypass that most of the time by caching unused patches and just reusing them in the future
|
|
22
|
+
* if they are requested again.
|
|
23
|
+
*
|
|
24
|
+
* The caching strategy here is a little unusual. The texture atlas has a certain size, and the cache will use as much of
|
|
25
|
+
* the atlas space as it can. Meaning that if there is not much unused space - the cache will be very small
|
|
26
|
+
* And if only a small part of the atlas is area is used by "live" patches, the cache can be quite large
|
|
8
27
|
*/
|
|
9
|
-
const DEFAULT_CACHE_SIZE = 4194304;
|
|
10
|
-
|
|
11
28
|
export class CachingTextureAtlas extends AbstractTextureAtlas {
|
|
12
29
|
/**
|
|
13
30
|
*
|
|
@@ -15,23 +32,16 @@ export class CachingTextureAtlas extends AbstractTextureAtlas {
|
|
|
15
32
|
* @param {number} cache_size
|
|
16
33
|
*/
|
|
17
34
|
constructor({
|
|
18
|
-
atlas
|
|
19
|
-
cache_size = DEFAULT_CACHE_SIZE
|
|
35
|
+
atlas
|
|
20
36
|
}) {
|
|
21
37
|
super();
|
|
22
38
|
|
|
23
39
|
/**
|
|
24
40
|
*
|
|
25
|
-
* @type {
|
|
41
|
+
* @type {PatchRecord[]}
|
|
26
42
|
* @private
|
|
27
43
|
*/
|
|
28
|
-
this.__cached_patches =
|
|
29
|
-
maxWeight: cache_size,
|
|
30
|
-
keyHashFunction: invokeObjectHash,
|
|
31
|
-
keyWeigher(sampler) {
|
|
32
|
-
return sampler.computeByteSize();
|
|
33
|
-
}
|
|
34
|
-
});
|
|
44
|
+
this.__cached_patches = [];
|
|
35
45
|
|
|
36
46
|
/**
|
|
37
47
|
*
|
|
@@ -40,17 +50,156 @@ export class CachingTextureAtlas extends AbstractTextureAtlas {
|
|
|
40
50
|
*/
|
|
41
51
|
this.__atals = atlas;
|
|
42
52
|
|
|
43
|
-
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Finds an entry in the cache to evict
|
|
57
|
+
* @returns {number}
|
|
58
|
+
* @private
|
|
59
|
+
*/
|
|
60
|
+
__find_eviction_target() {
|
|
61
|
+
let best_date = Infinity;
|
|
62
|
+
let best_node = -1;
|
|
63
|
+
|
|
64
|
+
const records = this.__cached_patches;
|
|
65
|
+
const n = records.length;
|
|
66
|
+
|
|
67
|
+
for (let i = 0; i < n; i++) {
|
|
68
|
+
const record = records[i];
|
|
69
|
+
|
|
70
|
+
const date = record.last_use_time;
|
|
71
|
+
|
|
72
|
+
if (date < best_date) {
|
|
73
|
+
best_node = i;
|
|
74
|
+
best_date = date;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return best_node;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Finds an entry in the cache that could be replaced with an incoming element
|
|
84
|
+
* @private
|
|
85
|
+
* @returns {number}
|
|
86
|
+
* @param {number} width
|
|
87
|
+
* @param {number} height
|
|
88
|
+
*/
|
|
89
|
+
__find_replacement_target(width, height) {
|
|
90
|
+
// find the smallest element in area that is least recently used in cache
|
|
91
|
+
let best_area = Infinity;
|
|
92
|
+
let best_date = Infinity;
|
|
93
|
+
|
|
94
|
+
let best_node = -1;
|
|
95
|
+
|
|
96
|
+
const records = this.__cached_patches;
|
|
97
|
+
const n = records.length;
|
|
98
|
+
for (let i = 0; i < n; i++) {
|
|
99
|
+
const record = records[i];
|
|
100
|
+
|
|
101
|
+
const packing = record.patch.packing;
|
|
102
|
+
|
|
103
|
+
const w = packing.x1 - packing.x0;
|
|
104
|
+
const h = packing.y1 - packing.y0;
|
|
105
|
+
|
|
106
|
+
if (w < width || h < height) {
|
|
107
|
+
// too small
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const area = w * h;
|
|
112
|
+
|
|
113
|
+
if (area > best_area) {
|
|
114
|
+
// prefer smaller patches to evict, as they are cheaper to pull back later on
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const date = record.last_use_time;
|
|
119
|
+
|
|
120
|
+
if (date >= best_date) {
|
|
121
|
+
// prefer patches that have been in cache the longest (the oldest records)
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// found a better candidate, update
|
|
126
|
+
|
|
127
|
+
best_area = area;
|
|
128
|
+
best_date = date;
|
|
129
|
+
|
|
130
|
+
best_node = i;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return best_node;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Will evict specified element from cache
|
|
138
|
+
* @private
|
|
139
|
+
* @param {number} index
|
|
140
|
+
*/
|
|
141
|
+
__evict(index) {
|
|
142
|
+
const records = this.__cached_patches;
|
|
143
|
+
const record = records[index];
|
|
144
|
+
|
|
145
|
+
records.splice(index, 1);
|
|
146
|
+
this.__atals.remove(record.patch);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Evict all cached elements, purging the cache
|
|
151
|
+
* @private
|
|
152
|
+
*/
|
|
153
|
+
__evict_all() {
|
|
154
|
+
for (let i = this.__cached_patches.length - 1; i > 0; i--) {
|
|
155
|
+
this.__evict(i);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Free up space in cache to accommodate certain area
|
|
161
|
+
* @private
|
|
162
|
+
* @param {number} width
|
|
163
|
+
* @param {number} height
|
|
164
|
+
* @returns {boolean} true if enough space was freed up to fit specified area, false otherwise
|
|
165
|
+
*/
|
|
166
|
+
__evict_for(width, height) {
|
|
167
|
+
if (this.__cached_patches.length <= 0) {
|
|
168
|
+
// nothing to evict
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const target = this.__find_replacement_target(width, height);
|
|
173
|
+
|
|
174
|
+
if (target) {
|
|
175
|
+
// evicted just one element
|
|
176
|
+
this.__evict(target);
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
while (!this.__atals.can_pack(width, height)) {
|
|
181
|
+
const victim = this.__find_eviction_target();
|
|
182
|
+
|
|
183
|
+
if (victim === -1) {
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
this.__evict(victim);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return true;
|
|
44
191
|
}
|
|
45
192
|
|
|
46
193
|
update() {
|
|
47
194
|
try {
|
|
48
195
|
this.__atals.update();
|
|
49
196
|
} catch (e) {
|
|
50
|
-
|
|
197
|
+
// failed to perform update
|
|
198
|
+
|
|
199
|
+
if (this.__cached_patches.length > 0) {
|
|
51
200
|
|
|
52
201
|
// has some caches patches, lets drop them
|
|
53
|
-
this.
|
|
202
|
+
this.__evict_all();
|
|
54
203
|
|
|
55
204
|
// try to update again
|
|
56
205
|
this.__atals.update();
|
|
@@ -78,40 +227,56 @@ export class CachingTextureAtlas extends AbstractTextureAtlas {
|
|
|
78
227
|
return this.__atals.sampler;
|
|
79
228
|
}
|
|
80
229
|
|
|
81
|
-
/**
|
|
82
|
-
*
|
|
83
|
-
* @param {Sampler2D} sampler
|
|
84
|
-
* @param {AtlasPatch} patch
|
|
85
|
-
* @private
|
|
86
|
-
*/
|
|
87
|
-
__handle_cache_eviction(sampler, patch) {
|
|
88
|
-
this.__atals.remove(patch);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
230
|
reset() {
|
|
92
|
-
this.__cached_patches.
|
|
231
|
+
this.__cached_patches.splice(i, this.__cached_patches.length);
|
|
93
232
|
this.__atals.reset();
|
|
94
233
|
}
|
|
95
234
|
|
|
96
235
|
/**
|
|
97
236
|
*
|
|
98
237
|
* @param {Sampler2D} sampler
|
|
99
|
-
* @returns {
|
|
238
|
+
* @returns {number}
|
|
239
|
+
* @private
|
|
100
240
|
*/
|
|
101
|
-
|
|
102
|
-
const
|
|
241
|
+
__find_cache_record_index(sampler) {
|
|
242
|
+
const records = this.__cached_patches;
|
|
243
|
+
const n = records.length;
|
|
244
|
+
for (let i = 0; i < n; i++) {
|
|
245
|
+
if (records[i].patch.sampler === sampler) {
|
|
246
|
+
return i;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return -1;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
add(sampler, padding = 4) {
|
|
254
|
+
const existing_patch_index = this.__find_cache_record_index(sampler);
|
|
255
|
+
|
|
256
|
+
const records = this.__cached_patches;
|
|
103
257
|
|
|
104
|
-
if (
|
|
258
|
+
if (existing_patch_index !== -1) {
|
|
105
259
|
// cache hit
|
|
106
260
|
|
|
261
|
+
const record = records[existing_patch_index];
|
|
262
|
+
|
|
107
263
|
// remove from cache
|
|
108
|
-
|
|
264
|
+
records.splice(existing_patch_index, 1);
|
|
109
265
|
|
|
110
266
|
// return patch
|
|
111
|
-
return
|
|
267
|
+
return record.patch;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const w = sampler.width + padding * 2;
|
|
271
|
+
const h = sampler.height + padding * 2;
|
|
272
|
+
|
|
273
|
+
if (records.length > 0 && !this.__atals.can_pack(w, h)) {
|
|
274
|
+
// new element will not fit, lets try to evict some cache to try and accommodate it
|
|
275
|
+
// note that eviction will purge the entire cache in case that this patch doesn't fit
|
|
276
|
+
this.__evict_for(w, h);
|
|
112
277
|
}
|
|
113
278
|
|
|
114
|
-
return this.__atals.add(sampler);
|
|
279
|
+
return this.__atals.add(sampler, padding);
|
|
115
280
|
}
|
|
116
281
|
|
|
117
282
|
/**
|
|
@@ -126,7 +291,12 @@ export class CachingTextureAtlas extends AbstractTextureAtlas {
|
|
|
126
291
|
}
|
|
127
292
|
|
|
128
293
|
// push to cache, let eviction logic handle the rest
|
|
129
|
-
|
|
294
|
+
const record = new PatchRecord();
|
|
295
|
+
|
|
296
|
+
record.last_use_time = performance.now();
|
|
297
|
+
record.patch = patch;
|
|
298
|
+
|
|
299
|
+
this.__cached_patches.push(record);
|
|
130
300
|
|
|
131
301
|
return true;
|
|
132
302
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { MaxRectanglesPacker } from "../../../../core/geom/packing/MaxRectangles.js";
|
|
1
|
+
import { MaxRectanglesPacker } from "../../../../core/geom/packing/max-rect/MaxRectangles.js";
|
|
2
2
|
import { max2 } from "../../../../core/math/max2.js";
|
|
3
|
-
import { min2 } from "../../../../core/math/min2.js";
|
|
4
3
|
import { Sampler2D } from "../sampler/Sampler2D.js";
|
|
5
4
|
import IdPool from "../../../../core/IdPool.js";
|
|
6
5
|
import Vector2 from "../../../../core/geom/Vector2.js";
|
|
@@ -14,6 +13,7 @@ import {
|
|
|
14
13
|
} from "../../../../core/collection/table/DataType2TypedArrayConstructorMapping.js";
|
|
15
14
|
import { AbstractTextureAtlas } from "./AbstractTextureAtlas.js";
|
|
16
15
|
import { invokeObjectClone } from "../../../../core/model/object/invokeObjectClone.js";
|
|
16
|
+
import { ceilPowerOfTwo } from "../../../../core/binary/operations/ceilPowerOfTwo.js";
|
|
17
17
|
|
|
18
18
|
export class TextureAtlas extends AbstractTextureAtlas {
|
|
19
19
|
/**
|
|
@@ -161,15 +161,12 @@ export class TextureAtlas extends AbstractTextureAtlas {
|
|
|
161
161
|
if (patch.getFlag(AtlasPatchFlag.Packed)) {
|
|
162
162
|
//only care about packed patches
|
|
163
163
|
|
|
164
|
-
const
|
|
165
|
-
const
|
|
164
|
+
const x1 = patch.packing.x1;
|
|
165
|
+
const y1 = patch.packing.y1;
|
|
166
166
|
|
|
167
|
-
|
|
168
|
-
const y1 = pp.y + ps.y + patch.padding;
|
|
169
|
-
|
|
170
|
-
if (x1 >= x || y1 >= y) {
|
|
167
|
+
if (x1 > x || y1 > y) {
|
|
171
168
|
// patch bounds would violate the desired atlas extends
|
|
172
|
-
//Resizing atlas would result in some patches not fitting
|
|
169
|
+
// Resizing atlas would result in some patches not fitting
|
|
173
170
|
return false;
|
|
174
171
|
}
|
|
175
172
|
}
|
|
@@ -278,6 +275,16 @@ export class TextureAtlas extends AbstractTextureAtlas {
|
|
|
278
275
|
return patch;
|
|
279
276
|
}
|
|
280
277
|
|
|
278
|
+
/**
|
|
279
|
+
* Whether a patch of certain size could be packed without triggering repack or growing the atlas
|
|
280
|
+
* @param {number} w
|
|
281
|
+
* @param {number} h
|
|
282
|
+
* @returns {boolean}
|
|
283
|
+
*/
|
|
284
|
+
can_pack(w, h) {
|
|
285
|
+
return this.packer.canAdd(w, h);
|
|
286
|
+
}
|
|
287
|
+
|
|
281
288
|
/**
|
|
282
289
|
*
|
|
283
290
|
* @param {AtlasPatch} patch
|
|
@@ -482,32 +489,32 @@ export class TextureAtlas extends AbstractTextureAtlas {
|
|
|
482
489
|
// console.group("TextureAtlas.update");
|
|
483
490
|
// console.time('TextureAtlas.update');
|
|
484
491
|
|
|
485
|
-
const maxPower = 14;
|
|
492
|
+
const maxPower = 14;
|
|
486
493
|
|
|
487
|
-
const
|
|
488
|
-
|
|
489
|
-
0,
|
|
490
|
-
Math.log2(
|
|
491
|
-
min2(this.size.x, this.size.y)
|
|
492
|
-
)
|
|
493
|
-
)
|
|
494
|
-
);
|
|
494
|
+
const initial_p_x = max2(0, Math.log2(ceilPowerOfTwo(this.size.x)));
|
|
495
|
+
const initial_p_y = max2(0, Math.log2(ceilPowerOfTwo(this.size.y)));
|
|
495
496
|
|
|
496
|
-
let
|
|
497
|
+
let power_x = initial_p_x;
|
|
498
|
+
let power_y = initial_p_y;
|
|
497
499
|
|
|
498
500
|
while (!this.pack()) {
|
|
499
501
|
//packing failed, grow canvas
|
|
500
502
|
|
|
501
503
|
for (; ;) {
|
|
502
|
-
|
|
504
|
+
if (power_x < power_y) {
|
|
505
|
+
power_x++;
|
|
506
|
+
} else {
|
|
507
|
+
power_y++;
|
|
508
|
+
}
|
|
503
509
|
|
|
504
|
-
if (
|
|
505
|
-
throw new Error(`Packing failed, could not pack ${this.patches.length} into ${Math.pow(2, maxPower)} resolution texture. Initial
|
|
510
|
+
if (power_x > maxPower || power_y > maxPower) {
|
|
511
|
+
throw new Error(`Packing failed, could not pack ${this.patches.length} into ${Math.pow(2, maxPower)} resolution texture. Initial powers: ${initial_p_x}, ${initial_p_y}`);
|
|
506
512
|
}
|
|
507
513
|
|
|
508
|
-
const
|
|
514
|
+
const size_x = Math.pow(2, power_x);
|
|
515
|
+
const size_y = Math.pow(2, power_y);
|
|
509
516
|
|
|
510
|
-
if (this.resize(
|
|
517
|
+
if (this.resize(size_x, size_y)) {
|
|
511
518
|
break;
|
|
512
519
|
}
|
|
513
520
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AtlasPatch } from "../AtlasPatch.js";
|
|
2
2
|
import IdPool from "../../../../../core/IdPool.js";
|
|
3
|
-
import { MaxRectanglesPacker } from "../../../../../core/geom/packing/MaxRectangles.js";
|
|
3
|
+
import { MaxRectanglesPacker } from "../../../../../core/geom/packing/max-rect/MaxRectangles.js";
|
|
4
4
|
import {
|
|
5
5
|
ClampToEdgeWrapping,
|
|
6
6
|
DataTexture,
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @see https://github.com/terifan/ImageResampler/blob/dbc212ce6aaa769bf3c9623cb6ead58ffd51d76c/src/org/terifan/image_resampler/FilterFactory.java#L330
|
|
3
|
+
* @param {number} x
|
|
4
|
+
* @returns {number}
|
|
5
|
+
*/
|
|
6
|
+
export function box(x) {
|
|
7
|
+
if (x < -0.5) {
|
|
8
|
+
return 0;
|
|
9
|
+
}
|
|
10
|
+
if (x <= 0.5) {
|
|
11
|
+
return 1;
|
|
12
|
+
}
|
|
13
|
+
return 0;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
box.support = 0.5;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @see https://github.com/terifan/ImageResampler/blob/dbc212ce6aaa769bf3c9623cb6ead58ffd51d76c/src/org/terifan/image_resampler/FilterFactory.java#L431
|
|
3
|
+
* @see https://www.intel.com/content/www/us/en/develop/documentation/ipp-dev-reference/top/volume-2-image-processing/ipp-ref-interpolation-in-image-geometry-transform/interpolation-with-two-parameter-cubic-filters.html
|
|
4
|
+
* @param {number} x
|
|
5
|
+
* @returns {number}
|
|
6
|
+
*/
|
|
7
|
+
export function cubic2(x){
|
|
8
|
+
const b = 0;
|
|
9
|
+
const c = 1;
|
|
10
|
+
|
|
11
|
+
if (x < -2.0)
|
|
12
|
+
{
|
|
13
|
+
return 0;
|
|
14
|
+
}
|
|
15
|
+
if (x < -1.0)
|
|
16
|
+
{
|
|
17
|
+
return Math.pow((12 - 9*b - 6*c) * Math.abs(x), 3) + Math.pow((-18 + 12*b + 6*c) * Math.abs(x), 2) + (6 - 2 * b) / 6;
|
|
18
|
+
}
|
|
19
|
+
if (x < 0.0)
|
|
20
|
+
{
|
|
21
|
+
return Math.pow((-b-6*c)*Math.abs(x), 3) + Math.pow((6*b+30*c)*Math.abs(x),2) + (-12*b-48*c)*Math.abs(x)+(8*b+24*c);
|
|
22
|
+
}
|
|
23
|
+
if (x < 1.0)
|
|
24
|
+
{
|
|
25
|
+
return Math.pow((-b-6*c)*Math.abs(x), 3) + Math.pow((6*b+30*c)*Math.abs(x),2) + (-12*b-48*c)*Math.abs(x)+(8*b+24*c);
|
|
26
|
+
}
|
|
27
|
+
if (x < 2.0)
|
|
28
|
+
{
|
|
29
|
+
return Math.pow((12 - 9*b - 6*c) * Math.abs(x), 3) + Math.pow((-18 + 12*b + 6*c) * Math.abs(x), 2) + (6 - 2 * b);
|
|
30
|
+
}
|
|
31
|
+
return 0;
|
|
32
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Precomputed constant term
|
|
3
|
+
* @type {number}
|
|
4
|
+
*/
|
|
5
|
+
const a = Math.sqrt(2.0 / Math.PI);
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
*
|
|
9
|
+
* @param {number} x
|
|
10
|
+
* @returns {number}
|
|
11
|
+
*/
|
|
12
|
+
export function gaussian(x) {
|
|
13
|
+
return a * Math.exp(-2.0 * x * x);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
gaussian.support = 1.25;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { bessel_i0 } from "../../../../../core/math/bessel_i0.js";
|
|
2
|
+
import { clamp } from "../../../../../core/math/clamp.js";
|
|
3
|
+
|
|
4
|
+
const BASE_I0 = bessel_i0(6.5);
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Support kernel size is 1
|
|
8
|
+
* @see https://github.com/terifan/ImageResampler/blob/dbc212ce6aaa769bf3c9623cb6ead58ffd51d76c/src/org/terifan/image_resampler/FilterFactory.java#L815
|
|
9
|
+
* @param {number} x
|
|
10
|
+
* @returns {number}
|
|
11
|
+
*/
|
|
12
|
+
export function kaiser_1(x) {
|
|
13
|
+
const i = clamp(x * 0.6666666666666666, -1.0, 1.0);
|
|
14
|
+
const i0a = 1 / BASE_I0;
|
|
15
|
+
|
|
16
|
+
return bessel_i0(6.5 * Math.sqrt(1 - i * i)) * i0a;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
kaiser_1.support = 1;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { bessel_3 } from "../../../../../core/math/bessel_3.js";
|
|
2
|
+
import { clamp } from "../../../../../core/math/clamp.js";
|
|
3
|
+
|
|
4
|
+
const PI3 = 3.0 * Math.PI;
|
|
5
|
+
const BESSEL_3_PI_3 = bessel_3(PI3);
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
*
|
|
9
|
+
* Kaiser Bessel window
|
|
10
|
+
* @see https://bgolus.medium.com/sharper-mipmapping-using-shader-based-supersampling-ed7aadb47bec#5bfb:~:text=Kaiser%2Dfiltered%20Mipmaps
|
|
11
|
+
* @see https://www.shadertoy.com/view/wlf3RH
|
|
12
|
+
*/
|
|
13
|
+
export function kaiser_bessel_window(x) {
|
|
14
|
+
const i = clamp(x * 0.6666666666666666, -1.0, 1.0);
|
|
15
|
+
const t = Math.sqrt(1.0 - i * i);
|
|
16
|
+
return bessel_3(PI3 * t) / BESSEL_3_PI_3;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
kaiser_bessel_window.support = 2;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
const b = 1.0 / 3.0;
|
|
2
|
+
const c = 1.0 / 3.0;
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @see Don P. Mitchell, Arun N. Netravali. Reconstruction Filters in Computer Graphics. Computer Graphics, Volume 22, Number 4, AT&T Bell Laboratories, Murray Hill, New Jersey, August 1988
|
|
6
|
+
* @see https://github.com/terifan/ImageResampler/blob/dbc212ce6aaa769bf3c9623cb6ead58ffd51d76c/src/org/terifan/image_resampler/FilterFactory.java#L540
|
|
7
|
+
* @param {number} x
|
|
8
|
+
* @returns {number}
|
|
9
|
+
*/
|
|
10
|
+
export function mitchell(x) {
|
|
11
|
+
const p0 = (6.0 - 2.0 * b) / 6.0;
|
|
12
|
+
const p2 = (-18.0 + 12.0 * b + 6.0 * c) / 6.0;
|
|
13
|
+
const p3 = (12.0 - 9.0 * b - 6.0 * c) / 6.0;
|
|
14
|
+
const q0 = (8.0 * b + 24.0 * c) / 6.0;
|
|
15
|
+
const q1 = (-12.0 * b - 48.0 * c) / 6.0;
|
|
16
|
+
const q2 = (6.0 * b + 30.0 * c) / 6.0;
|
|
17
|
+
const q3 = (-b - 6.0 * c) / 6.0;
|
|
18
|
+
|
|
19
|
+
if (x < -2.0) {
|
|
20
|
+
return 0.0;
|
|
21
|
+
}
|
|
22
|
+
if (x < -1.0) {
|
|
23
|
+
return q0 - x * (q1 - x * (q2 - x * q3));
|
|
24
|
+
}
|
|
25
|
+
if (x < 0.0) {
|
|
26
|
+
return p0 + x * x * (p2 - x * p3);
|
|
27
|
+
}
|
|
28
|
+
if (x < 1.0) {
|
|
29
|
+
return p0 + x * x * (p2 + x * p3);
|
|
30
|
+
}
|
|
31
|
+
if (x < 2.0) {
|
|
32
|
+
return q0 + x * (q1 + x * (q2 + x * q3));
|
|
33
|
+
}
|
|
34
|
+
return 0.0;
|
|
35
|
+
}
|
|
36
|
+
mitchell.support = 2;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Hand-optimized mitchell function with c = 0.3333333 and b = 0.3333333
|
|
40
|
+
* NOTE: assumes non-negative values only
|
|
41
|
+
* @param {number} x
|
|
42
|
+
* @returns {number}
|
|
43
|
+
*/
|
|
44
|
+
export function mitchell_v1(x) {
|
|
45
|
+
|
|
46
|
+
if (x < 1.0) {
|
|
47
|
+
return 0.8888888888888888 + x * x * (-2 + x * 1.1666666666666667);
|
|
48
|
+
}
|
|
49
|
+
if (x < 2.0) {
|
|
50
|
+
return 1.7777777777777777 + x * (-3.3333333333333335 + x * (2 + x * -0.3888888888888889));
|
|
51
|
+
}
|
|
52
|
+
return 0.0;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
mitchell_v1.support = 2;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { max2 } from "../../../../../core/math/max2.js";
|
|
2
|
+
import { min2 } from "../../../../../core/math/min2.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
*
|
|
6
|
+
* @param {Sampler2D} source
|
|
7
|
+
* @param {Sampler2D} destination
|
|
8
|
+
* @param {(number)=>number|{support:number}} weight_computer
|
|
9
|
+
* @param {number} support size of support kernel
|
|
10
|
+
*/
|
|
11
|
+
export function sampler2d_scale_down_generic(source, destination, weight_computer, support = weight_computer.support) {
|
|
12
|
+
|
|
13
|
+
const d_w = destination.width;
|
|
14
|
+
const d_h = destination.height;
|
|
15
|
+
|
|
16
|
+
const item_size = destination.itemSize;
|
|
17
|
+
|
|
18
|
+
const source_width = source.width;
|
|
19
|
+
const source_height = source.height;
|
|
20
|
+
|
|
21
|
+
if (source_width < d_w || source_height < d_h) {
|
|
22
|
+
throw new Error('Invalid scale, source is larger than destination in at least 1 dimension');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const source_data = source.data;
|
|
26
|
+
const destination_data = destination.data;
|
|
27
|
+
|
|
28
|
+
const ratio_x = source_width / d_w;
|
|
29
|
+
const ratio_y = source_height / d_h;
|
|
30
|
+
|
|
31
|
+
const rcp_ratio_y = 2 / ratio_x;
|
|
32
|
+
const rcp_ratio_x = 2 / ratio_x;
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
const range2_x = Math.ceil(ratio_x * support / 2);
|
|
36
|
+
const range2_y = Math.ceil(ratio_y * support / 2);
|
|
37
|
+
|
|
38
|
+
const sample = new Array(item_size);
|
|
39
|
+
|
|
40
|
+
let v, u, i, j, k;
|
|
41
|
+
|
|
42
|
+
for (v = 0; v < d_h; v++) {
|
|
43
|
+
|
|
44
|
+
const center_y = (v + 0.5) * ratio_y;
|
|
45
|
+
const i_center_y = Math.floor(center_y);
|
|
46
|
+
//
|
|
47
|
+
const source_y0 = max2(0, i_center_y - range2_y);
|
|
48
|
+
const source_y1 = min2(i_center_y + range2_y, source_height - 1);
|
|
49
|
+
|
|
50
|
+
for (u = 0; u < d_w; u++) {
|
|
51
|
+
const center_x = (u + 0.5) * ratio_x;
|
|
52
|
+
const i_center_x = Math.floor(center_x);
|
|
53
|
+
|
|
54
|
+
const source_x0 = max2(i_center_x - range2_x, 0);
|
|
55
|
+
const source_x1 = min2(i_center_x + range2_x, source_width - 1);
|
|
56
|
+
|
|
57
|
+
let z = 0;
|
|
58
|
+
// reset sample
|
|
59
|
+
for (i = 0; i < item_size; i++) {
|
|
60
|
+
sample[i] = 0;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
for (j = source_y0; j <= source_y1; j++) {
|
|
64
|
+
|
|
65
|
+
const f_y = (j - center_y);
|
|
66
|
+
|
|
67
|
+
const f_y_rcp = f_y * rcp_ratio_y;
|
|
68
|
+
const f_y_rcp2 = f_y_rcp * f_y_rcp;
|
|
69
|
+
|
|
70
|
+
for (i = source_x0; i <= source_x1; i++) {
|
|
71
|
+
|
|
72
|
+
const f_x = (i - center_x);
|
|
73
|
+
|
|
74
|
+
const f_x_rcp = f_x * rcp_ratio_x;
|
|
75
|
+
const f_x_rcp2 = f_x_rcp * f_x_rcp;
|
|
76
|
+
|
|
77
|
+
const distance_from_center_sqr = f_x_rcp2 + f_y_rcp2;
|
|
78
|
+
const distance_from_center = Math.sqrt(distance_from_center_sqr);
|
|
79
|
+
|
|
80
|
+
const weight = weight_computer(distance_from_center);
|
|
81
|
+
|
|
82
|
+
if (weight <= 0) {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
z += weight;
|
|
87
|
+
|
|
88
|
+
const source_texel_address = (j * source_width + i) * item_size;
|
|
89
|
+
|
|
90
|
+
for (k = 0; k < item_size; k++) {
|
|
91
|
+
sample[k] += weight * source_data[source_texel_address + k];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
// dilute sample and write to destination
|
|
99
|
+
const inv_z = 1 / z;
|
|
100
|
+
|
|
101
|
+
const texel_address = (v * d_w + u) * item_size;
|
|
102
|
+
|
|
103
|
+
for (i = 0; i < item_size; i++) {
|
|
104
|
+
destination_data[texel_address + i] = sample[i] * inv_z;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
}
|