@woosh/meep-engine 2.40.0 → 2.41.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/BinaryBuffer.js +6 -1
- package/core/collection/HashMap.d.ts +2 -0
- package/core/collection/HashMap.js +22 -0
- package/core/geom/3d/normal/hemioct/encode_unit3_hemioct.js +5 -0
- package/core/geom/3d/normal/octahedron/decode_octahedron_to_unit.js +31 -0
- package/core/geom/3d/normal/octahedron/encode_unit_to_octahedron.js +33 -0
- package/core/geom/3d/normal/octahedron/encoding.spec.js +29 -0
- package/core/geom/3d/topology/struct/BinaryTopology.js +112 -0
- package/core/geom/Quaternion.js +8 -8
- package/core/geom/Quaternion.spec.js +13 -0
- package/core/geom/Vector1.d.ts +2 -0
- package/core/math/sign_not_zero.js +8 -0
- package/core/parser/simple/SimpleParser.js +3 -86
- package/core/parser/simple/readUnsignedInteger.js +66 -0
- package/core/parser/simple/skipWhitespace.js +21 -0
- package/editor/view/ecs/components/common/NumberController.js +24 -6
- package/engine/EngineHarness.js +6 -1
- package/engine/asset/AssetDescription.spec.js +27 -0
- package/engine/asset/loaders/GLTFAssetLoader.js +5 -3
- package/engine/asset/loaders/image/png/PNG.js +6 -0
- package/engine/asset/loaders/image/png/PNGReader.js +41 -0
- package/engine/ecs/foliage/ImpostorFoliage.js +4 -0
- package/engine/ecs/terrain/tiles/TerrainTile.js +2 -0
- package/engine/graphics/ecs/mesh-v2/three_object_to_entity_composition.js +22 -7
- package/engine/graphics/filter/ImageFilter.js +2 -1
- package/engine/graphics/geometry/MikkT/MikkTSpace.spec.js +7 -1
- package/engine/{asset/loaders/geometry/geometry → graphics/geometry/buffered}/computeBufferAttributeHash.js +1 -1
- package/engine/{asset/loaders/geometry/geometry → graphics/geometry/buffered}/computeGeometryEquality.js +2 -2
- package/engine/{asset/loaders/geometry/geometry → graphics/geometry/buffered}/computeGeometryHash.js +1 -1
- package/engine/graphics/geometry/optimization/merge/merge_geometry_hierarchy.js +2 -2
- package/engine/graphics/impostors/card_cluster/FacePlaneAssignment.js +30 -0
- package/engine/graphics/impostors/card_cluster/README.md +13 -0
- package/engine/graphics/impostors/octahedral/ImpostorBaker.js +332 -0
- package/engine/graphics/impostors/octahedral/ImpostorCaptureType.js +10 -0
- package/engine/graphics/impostors/octahedral/ImpostorDescription.js +63 -0
- package/engine/graphics/impostors/octahedral/README.md +29 -0
- package/engine/graphics/impostors/octahedral/bake/compute_bounding_sphere.js +42 -0
- package/engine/graphics/impostors/octahedral/bake/prepare_bake_material.js +88 -0
- package/engine/graphics/impostors/octahedral/grid/HemiOctahedralUvEncoder.js +20 -0
- package/engine/graphics/impostors/octahedral/grid/OctahedralUvEncoder.js +25 -0
- package/engine/graphics/impostors/octahedral/grid/UvEncoder.js +21 -0
- package/engine/graphics/impostors/octahedral/prototypeBaker.js +138 -0
- package/engine/graphics/impostors/octahedral/shader/BakeShaderStandard.js +157 -0
- package/engine/graphics/impostors/octahedral/shader/ImpostorShaderStandard.js +306 -0
- package/engine/graphics/impostors/octahedral/shader/glsl/common.glsl +206 -0
- package/engine/graphics/micron/convert_three_object_to_micron.js +2 -2
- package/engine/graphics/micron/plugin/MicronRenderPlugin.js +2 -2
- package/engine/graphics/particles/node-based/codegen/CodeGenerator.js +1 -1
- package/engine/graphics/particles/node-based/nodes/noise/CurlNoiseNode.js +1 -1
- package/engine/graphics/render/forward_plus/LightManager.js +25 -0
- package/engine/graphics/render/forward_plus/data/TextureBackedMemoryRegion.js +6 -2
- package/engine/graphics/render/forward_plus/materials/FP_SHADER_CHUNK_ACCUMULATION.js +6 -11
- package/engine/graphics/render/forward_plus/prototype/prototypeLightManager.js +268 -2
- package/engine/graphics/render/visibility/hiz/buildCanvasViewFromTexture.js +32 -10
- package/engine/graphics/shaders/ScreenSpaceQuadShader.js +4 -14
- package/engine/graphics/shaders/glsl_gen_swizzled_read.js +39 -0
- package/engine/graphics/texture/atlas/AtlasPatch.js +12 -6
- package/engine/logging/ConsoleLoggerBackend.js +15 -0
- package/engine/logging/Logger.js +24 -1
- package/engine/logging/LoggerBackend.js +1 -0
- package/engine/physics/fluid/FluidField.js +9 -0
- package/engine/physics/fluid/effector/AbstractFluidEffector.js +6 -0
- package/engine/physics/fluid/effector/GlobalFluidEffector.js +12 -0
- package/engine/physics/fluid/effector/WakeFluidEffector.js +8 -0
- package/engine/ui/GUIEngine.js +8 -1
- package/package.json +1 -1
- package/view/tooltip/gml/parser/readReferenceToken.js +1 -1
- package/engine/asset/GameAssetManager.js +0 -137
|
@@ -11,7 +11,9 @@ import { MeshDepthMaterial, RGBADepthPacking } from "three";
|
|
|
11
11
|
import { TextureAttachmentsByMaterialType } from "./material/TextureAttachmensByMaterialType.js";
|
|
12
12
|
import { AssetLoader } from "./AssetLoader.js";
|
|
13
13
|
import { ensureGeometryBoundingSphere } from "../../graphics/geometry/buffered/ensureGeometryBoundingSphere.js";
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
computeSkinnedMeshBoundingVolumes
|
|
16
|
+
} from "../../graphics/geometry/skining/computeSkinnedMeshBoundingVolumes.js";
|
|
15
17
|
import { ensureGeometryBoundingBox } from "../../graphics/util/ensureGeometryBoundingBox.js";
|
|
16
18
|
import { cloneObject3D } from "../../graphics/three/cloneObject3D.js";
|
|
17
19
|
import GLTFTextureDDSExtension from "./gltf/extensions/MSFT_texture_dds.js";
|
|
@@ -20,8 +22,8 @@ import { computeObjectBoundingSphere } from "./gltf/computeObjectBoundingSphere.
|
|
|
20
22
|
import { isMesh } from "./gltf/isMesh.js";
|
|
21
23
|
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader.js";
|
|
22
24
|
import { HashSet } from "../../../core/collection/HashSet.js";
|
|
23
|
-
import { computeGeometryHash } from "
|
|
24
|
-
import { computeGeometryEquality } from "
|
|
25
|
+
import { computeGeometryHash } from "../../graphics/geometry/buffered/computeGeometryHash.js";
|
|
26
|
+
import { computeGeometryEquality } from "../../graphics/geometry/buffered/computeGeometryEquality.js";
|
|
25
27
|
import { traverseThreeObject } from "../../graphics/ecs/highlight/renderer/traverseThreeObject.js";
|
|
26
28
|
import { computeTextureHash } from "./material/computeTextureHash.js";
|
|
27
29
|
import { computeTextureEquality } from "./material/computeTextureEquality.js";
|
|
@@ -157,12 +157,53 @@ PNGReader.prototype.decodeChunk = function () {
|
|
|
157
157
|
case 'IEND':
|
|
158
158
|
this.decodeIEND(chunk);
|
|
159
159
|
break;
|
|
160
|
+
default:
|
|
161
|
+
//console.log(`Unsupported block ${type}`);
|
|
162
|
+
break;
|
|
160
163
|
}
|
|
161
164
|
|
|
162
165
|
return type;
|
|
163
166
|
|
|
164
167
|
};
|
|
165
168
|
|
|
169
|
+
/**
|
|
170
|
+
* https://www.w3.org/TR/PNG/#11tEXt
|
|
171
|
+
* @param {Uint8Array} chunk
|
|
172
|
+
*/
|
|
173
|
+
PNGReader.prototype.decodetEXt = function (chunk) {
|
|
174
|
+
const buff = BinaryBuffer.fromArrayBuffer(chunk.buffer);
|
|
175
|
+
const keyword = buff.readASCIICharacters(Number.POSITIVE_INFINITY, true);
|
|
176
|
+
|
|
177
|
+
const value = buff.readASCIICharacters(keyword.length - 1, false);
|
|
178
|
+
|
|
179
|
+
this.png.text[keyword] = value;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* NOTE: untested
|
|
183
|
+
* https://www.w3.org/TR/PNG/#11iEXt
|
|
184
|
+
* @param {Uint8Array} chunk
|
|
185
|
+
*/
|
|
186
|
+
PNGReader.prototype.decodeiEXt = function (chunk) {
|
|
187
|
+
const buff = BinaryBuffer.fromArrayBuffer(chunk.buffer);
|
|
188
|
+
|
|
189
|
+
const keyword = buff.readASCIICharacters(Number.POSITIVE_INFINITY, true);
|
|
190
|
+
|
|
191
|
+
const compression_flag = buff.readUint8();
|
|
192
|
+
const compression_method = buff.readUint8();
|
|
193
|
+
const language_tag = buff.readASCIICharacters(Number.POSITIVE_INFINITY, true);
|
|
194
|
+
const translated_keyword = buff.readUTF8String();
|
|
195
|
+
|
|
196
|
+
const separator = buff.readUint8();
|
|
197
|
+
|
|
198
|
+
if (separator !== 0) {
|
|
199
|
+
throw new Error('Expected Null Separator after Translated keyword');
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const text = buff.readUTF8String();
|
|
203
|
+
|
|
204
|
+
this.png.text[keyword] = text;
|
|
205
|
+
}
|
|
206
|
+
|
|
166
207
|
/**
|
|
167
208
|
* http://www.w3.org/TR/2003/REC-PNG-20031110/#11IHDR
|
|
168
209
|
* http://www.libpng.org/pub/png/spec/1.2/png-1.2-pdg.html#C.IHDR
|
|
@@ -19,6 +19,10 @@ import {
|
|
|
19
19
|
import Vector2 from "../../../core/geom/Vector2.js";
|
|
20
20
|
import Vector4 from "../../../core/geom/Vector4.js";
|
|
21
21
|
|
|
22
|
+
/**
|
|
23
|
+
* @deprecated use other options, such as ShadedMesh with INSTANCED draw mode or dedicated instanced mesh system such as Foliage2System
|
|
24
|
+
* @constructor
|
|
25
|
+
*/
|
|
22
26
|
const Foliage = function (options) {
|
|
23
27
|
const size = options.size || 1;
|
|
24
28
|
const lightMap = options.lightMap;
|
|
@@ -2,6 +2,22 @@ import { EntityNode } from "../../../ecs/parent/EntityNode.js";
|
|
|
2
2
|
import { Transform } from "../../../ecs/transform/Transform.js";
|
|
3
3
|
import { ShadedGeometry } from "./ShadedGeometry.js";
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
*
|
|
7
|
+
* @param {Transform} transform
|
|
8
|
+
* @param {THREE.Object3D} three_object
|
|
9
|
+
*/
|
|
10
|
+
function copy_three_transform(transform, three_object) {
|
|
11
|
+
|
|
12
|
+
// copy object transform
|
|
13
|
+
transform.position.copy(three_object.position);
|
|
14
|
+
transform.scale.copy(three_object.scale);
|
|
15
|
+
transform.rotation.copy(three_object.quaternion);
|
|
16
|
+
|
|
17
|
+
//
|
|
18
|
+
transform.matrix.set(three_object.matrix.elements);
|
|
19
|
+
}
|
|
20
|
+
|
|
5
21
|
/**
|
|
6
22
|
*
|
|
7
23
|
* @param {THREE.Object3D} root
|
|
@@ -13,16 +29,15 @@ export function three_object_to_entity_composition(root) {
|
|
|
13
29
|
const node_transform = node.transform;
|
|
14
30
|
|
|
15
31
|
// copy object transform
|
|
16
|
-
node_transform
|
|
17
|
-
node_transform.scale.copy(root.scale);
|
|
18
|
-
node_transform.rotation.copy(root.quaternion);
|
|
19
|
-
|
|
20
|
-
//
|
|
21
|
-
node_transform.matrix.set(root.matrix.elements);
|
|
32
|
+
copy_three_transform(node_transform, root);
|
|
22
33
|
|
|
23
34
|
const entity = node.entity;
|
|
24
35
|
|
|
25
|
-
|
|
36
|
+
const transform = new Transform();
|
|
37
|
+
// initialize world transform
|
|
38
|
+
transform.fromMatrix4(root.matrixWorld.elements);
|
|
39
|
+
|
|
40
|
+
entity.add(transform);
|
|
26
41
|
|
|
27
42
|
if (root.isMesh) {
|
|
28
43
|
if (root.isSkinnedMesh) {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
import { LinearFilter, Mesh, OrthographicCamera, Scene, ShaderMaterial, WebGLRenderTarget } from 'three';
|
|
6
|
+
import { DoubleSide, LinearFilter, Mesh, OrthographicCamera, Scene, ShaderMaterial, WebGLRenderTarget } from 'three';
|
|
7
7
|
import { FULL_SCREEN_TRIANGLE_GEOMETRY } from "../geometry/FULL_SCREEN_TRIANGLE_GEOMETRY.js";
|
|
8
8
|
import { FULL_SCREEN_QUAD_VERTEX_SHADER } from "../FULL_SCREEN_QUAD_VERTEX_SHADER.js";
|
|
9
9
|
|
|
@@ -41,6 +41,7 @@ function processTexture(
|
|
|
41
41
|
material.fog = false;
|
|
42
42
|
material.uniformsNeedUpdate = true;
|
|
43
43
|
material.needsUpdate = true;
|
|
44
|
+
material.side = DoubleSide;
|
|
44
45
|
|
|
45
46
|
const quad = new Mesh(FULL_SCREEN_TRIANGLE_GEOMETRY, material);
|
|
46
47
|
|
|
@@ -14,7 +14,13 @@ test('single triangle', () => {
|
|
|
14
14
|
|
|
15
15
|
ctx.geometry_buffer_index = [0, 1, 2];
|
|
16
16
|
ctx.geometry_buffer_vertex_position = [1, 2, 3, 4, 5, 6, 7, 8, 9];
|
|
17
|
-
|
|
17
|
+
|
|
18
|
+
ctx.geometry_buffer_vertex_normal = [
|
|
19
|
+
0, 1, 0,
|
|
20
|
+
0, 1, 0,
|
|
21
|
+
0, 1, 0
|
|
22
|
+
];
|
|
23
|
+
|
|
18
24
|
ctx.geometry_buffer_vertex_uv = [0, 0, 0, 0, 0, 0];
|
|
19
25
|
|
|
20
26
|
genTangSpace(ctx);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { isArrayEqualStrict } from "
|
|
2
|
-
import { is_typed_array_equals } from "
|
|
1
|
+
import { isArrayEqualStrict } from "../../../../core/collection/array/isArrayEqualStrict.d.ts";
|
|
2
|
+
import { is_typed_array_equals } from "../../../../core/collection/array/typed/is_typed_array_equals.js";
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
/**
|
package/engine/{asset/loaders/geometry/geometry → graphics/geometry/buffered}/computeGeometryHash.js
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { computeBufferAttributeHash } from "./computeBufferAttributeHash.js";
|
|
2
|
-
import { computeStringHash } from "
|
|
2
|
+
import { computeStringHash } from "../../../../core/primitives/strings/computeStringHash.d.ts";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
*
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { StaticMaterialCache } from "../../../../asset/loaders/material/StaticMaterialCache.js";
|
|
2
2
|
import { HashMap } from "../../../../../core/collection/HashMap.js";
|
|
3
|
-
import { computeGeometryEquality } from "
|
|
4
|
-
import { computeGeometryHash } from "
|
|
3
|
+
import { computeGeometryEquality } from "../../buffered/computeGeometryEquality.js";
|
|
4
|
+
import { computeGeometryHash } from "../../buffered/computeGeometryHash.js";
|
|
5
5
|
import { array_copy } from "../../../../../core/collection/array/copyArray.js";
|
|
6
6
|
import { mat4 } from "gl-matrix";
|
|
7
7
|
import { Group, Matrix4, Mesh } from "three";
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A geometry is separated into individual triangles, with each triangle projected onto a single plane.
|
|
3
|
+
* This class contains and fully describes such assignment
|
|
4
|
+
*/
|
|
5
|
+
export class FacePlaneAssignment {
|
|
6
|
+
/**
|
|
7
|
+
* Flat plane data array
|
|
8
|
+
* Format: [normal_0_x, normal_0_y, normal_0_z, plane_0_offset, normal_1_x, ... ]
|
|
9
|
+
* @type {number[]}
|
|
10
|
+
*/
|
|
11
|
+
planes = [];
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
*
|
|
15
|
+
* @type {number}
|
|
16
|
+
*/
|
|
17
|
+
plane_count = 0;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
*
|
|
21
|
+
* @type {THREE.BufferGeometry|null}
|
|
22
|
+
*/
|
|
23
|
+
geometry = null;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Indices of planes associated with each face (triangle) for a given face index
|
|
27
|
+
* @type {number[]}
|
|
28
|
+
*/
|
|
29
|
+
face_assignments = [];
|
|
30
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Card Cluster Impostors
|
|
2
|
+
|
|
3
|
+
The system is fairly trivial in concept. It generates a specified number of *cards* (quads) along with textures to represent original mesh.
|
|
4
|
+
|
|
5
|
+
The idea for this system was inspired by Sean Feeley's talk at GDC 2019 "Interactive Wind and Vegetation in 'God of War'", where he briefly explained how impostors work in their engine.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
### References
|
|
9
|
+
|
|
10
|
+
1. [2003] "Billboard Clouds for Extreme Model Simplification" by Xavier Decoret, Fr ´ edo Durand, Franc ´ ¸ois X. Sillion, Julie Dorsey
|
|
11
|
+
2. [2005] "Extreme Model Simplification for Forest Rendering" by Anton L. Fuhrmann, Eike Umlauf and Stephan Mantler
|
|
12
|
+
3. [2005] "Tree Rendering with Billboard Clouds" by Ismael Garcia, Mateu Sbert, and László Szirmay-Kalos
|
|
13
|
+
4. [2009] "Implementation of an Improved Billboard Cloud Algorithm" by Christian Luksch
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import { ImpostorCaptureType } from "./ImpostorCaptureType.js";
|
|
2
|
+
import { assert } from "../../../../core/assert.js";
|
|
3
|
+
import { isPowerOfTwo } from "../../../../core/math/isPowerOrTwo.js";
|
|
4
|
+
import { Mesh, OrthographicCamera, Scene, Vector4, WebGLMultipleRenderTargets } from "three";
|
|
5
|
+
import { ImpostorDescription } from "./ImpostorDescription.js";
|
|
6
|
+
import { mat4 } from "gl-matrix";
|
|
7
|
+
import { BakeShaderStandard } from "./shader/BakeShaderStandard.js";
|
|
8
|
+
import { Sampler2D } from "../../texture/sampler/Sampler2D.js";
|
|
9
|
+
import Signal from "../../../../core/events/signal/Signal.js";
|
|
10
|
+
import { HashMap } from "../../../../core/collection/HashMap.js";
|
|
11
|
+
import { computeMaterialHash } from "../../../asset/loaders/material/computeMaterialHash.js";
|
|
12
|
+
import { computeMaterialEquality } from "../../../asset/loaders/material/computeMaterialEquality.js";
|
|
13
|
+
import { UvEncoder } from "./grid/UvEncoder.js";
|
|
14
|
+
import { OctahedralUvEncoder } from "./grid/OctahedralUvEncoder.js";
|
|
15
|
+
import { HemiOctahedralUvEncoder } from "./grid/HemiOctahedralUvEncoder.js";
|
|
16
|
+
import { prepare_bake_material } from "./bake/prepare_bake_material.js";
|
|
17
|
+
import { compute_bounding_sphere } from "./bake/compute_bounding_sphere.js";
|
|
18
|
+
import { collectIteratorValueToArray } from "../../../../core/collection/IteratorUtils.js";
|
|
19
|
+
|
|
20
|
+
export class ImpostorBaker {
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
*
|
|
24
|
+
* @type {THREE.WebGLRenderer|null}
|
|
25
|
+
* @private
|
|
26
|
+
*/
|
|
27
|
+
_renderer = null;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
*
|
|
31
|
+
* @param {THREE.WebGLRenderer} v
|
|
32
|
+
*/
|
|
33
|
+
set renderer(v) {
|
|
34
|
+
this._renderer = v;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
*
|
|
40
|
+
* @param {number} distance
|
|
41
|
+
* @param {{mesh:ShadedGeometry, transform:mat4}[]} objects
|
|
42
|
+
* @param {number} resolution
|
|
43
|
+
* @param {number} frames
|
|
44
|
+
* @param {UvEncoder} encoder
|
|
45
|
+
*/
|
|
46
|
+
bake_internal({
|
|
47
|
+
distance,
|
|
48
|
+
objects,
|
|
49
|
+
resolution,
|
|
50
|
+
frames,
|
|
51
|
+
encoder
|
|
52
|
+
}) {
|
|
53
|
+
|
|
54
|
+
assert.isNumber(distance, 'distance');
|
|
55
|
+
assert.isNonNegativeInteger(resolution, 'resolution');
|
|
56
|
+
assert.isNonNegativeInteger(frames, 'frames');
|
|
57
|
+
|
|
58
|
+
const unit_sphere_direction = [];
|
|
59
|
+
|
|
60
|
+
const cam = new OrthographicCamera();
|
|
61
|
+
const rt = new WebGLMultipleRenderTargets(resolution, resolution, 3);
|
|
62
|
+
rt.scissorTest = false;
|
|
63
|
+
rt.stencilBuffer = false;
|
|
64
|
+
|
|
65
|
+
rt.texture[0].name = 'diffuse+alpha';
|
|
66
|
+
rt.texture[1].name = 'normal+depth';
|
|
67
|
+
rt.texture[2].name = 'orm'; // Occlusion, Roughness, Metalness
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
const renderer = this._renderer;
|
|
71
|
+
|
|
72
|
+
// remember render state
|
|
73
|
+
const _rt = renderer.getRenderTarget();
|
|
74
|
+
const _vp = new Vector4();
|
|
75
|
+
renderer.getViewport(_vp);
|
|
76
|
+
const _autoClear = renderer.autoClear;
|
|
77
|
+
|
|
78
|
+
const gl = this._renderer.getContext();
|
|
79
|
+
|
|
80
|
+
// Baking should be done without anti-aliasing, so we make sure to disable it
|
|
81
|
+
const _enabled_antialias = gl.isEnabled(gl.SAMPLE_ALPHA_TO_COVERAGE);
|
|
82
|
+
|
|
83
|
+
if (_enabled_antialias) {
|
|
84
|
+
gl.disable(gl.SAMPLE_ALPHA_TO_COVERAGE);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
// set new render state
|
|
89
|
+
renderer.autoClear = false;
|
|
90
|
+
renderer.setRenderTarget(rt);
|
|
91
|
+
renderer.setClearColor(0xFFFFFF, 0);
|
|
92
|
+
renderer.clearColor();
|
|
93
|
+
|
|
94
|
+
const frame_width = resolution / frames;
|
|
95
|
+
const frame_height = resolution / frames;
|
|
96
|
+
|
|
97
|
+
const object_count = objects.length;
|
|
98
|
+
|
|
99
|
+
// construct scene
|
|
100
|
+
const scene = new Scene();
|
|
101
|
+
|
|
102
|
+
scene.autoUpdate = false;
|
|
103
|
+
scene.matrixAutoUpdate = false;
|
|
104
|
+
scene.matrixWorldNeedsUpdate = false;
|
|
105
|
+
|
|
106
|
+
const id = new ImpostorDescription();
|
|
107
|
+
id.frame_count = frames;
|
|
108
|
+
id.resolution = resolution;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Fired when all the rendering is done to indicate that cleanup should happen
|
|
112
|
+
* @type {Signal}
|
|
113
|
+
*/
|
|
114
|
+
const cleanup_signal = new Signal();
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
*
|
|
118
|
+
* @type {HashMap<Material, BakeShaderStandard>}
|
|
119
|
+
*/
|
|
120
|
+
const bake_material_map = new HashMap({
|
|
121
|
+
keyHashFunction: computeMaterialHash,
|
|
122
|
+
keyEqualityFunction: computeMaterialEquality
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
for (let k = 0; k < object_count; k++) {
|
|
127
|
+
const object = objects[k];
|
|
128
|
+
|
|
129
|
+
const source_mesh = object.mesh;
|
|
130
|
+
|
|
131
|
+
// ensure tangents are generated
|
|
132
|
+
// buffer_geometry_ensure_tangents(source_mesh.geometry);
|
|
133
|
+
|
|
134
|
+
const source_material = source_mesh.material;
|
|
135
|
+
|
|
136
|
+
// obtain material for baking
|
|
137
|
+
const bake_material = bake_material_map.getOrCompute(source_material, (source_material) => {
|
|
138
|
+
|
|
139
|
+
const bake_material = new BakeShaderStandard();
|
|
140
|
+
|
|
141
|
+
// prepare bake material to match the source material
|
|
142
|
+
prepare_bake_material(bake_material, source_material, cleanup_signal);
|
|
143
|
+
|
|
144
|
+
cleanup_signal.addOne(bake_material.dispose, bake_material);
|
|
145
|
+
|
|
146
|
+
id.source_material_count++;
|
|
147
|
+
|
|
148
|
+
return bake_material;
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
const geometry = source_mesh.geometry;
|
|
153
|
+
const mesh = new Mesh(geometry, bake_material);
|
|
154
|
+
|
|
155
|
+
mesh.matrixAutoUpdate = false;
|
|
156
|
+
mesh.matrixWorldNeedsUpdate = false;
|
|
157
|
+
mesh.frustumCulled = false;
|
|
158
|
+
|
|
159
|
+
mat4.copy(mesh.matrixWorld.elements, object.transform);
|
|
160
|
+
|
|
161
|
+
scene.add(mesh);
|
|
162
|
+
|
|
163
|
+
// update stats
|
|
164
|
+
id.source_geometry_polygon_count += geometry.getIndex().count / 3;
|
|
165
|
+
id.source_geometry_vertex_count += geometry.getAttribute('position').count;
|
|
166
|
+
id.source_instance_count++;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
const bake_material_array = collectIteratorValueToArray([], bake_material_map.values());
|
|
171
|
+
|
|
172
|
+
// traverse octahedron
|
|
173
|
+
const max_frame_index = frames - 1;
|
|
174
|
+
|
|
175
|
+
for (let i = 0; i < frames; i++) {
|
|
176
|
+
const octahedron_u = max_frame_index > 0 ? (i / max_frame_index) : 0;
|
|
177
|
+
|
|
178
|
+
for (let j = 0; j < frames; j++) {
|
|
179
|
+
|
|
180
|
+
const octahedron_v = max_frame_index > 0 ? (j / max_frame_index) : 0;
|
|
181
|
+
|
|
182
|
+
// compute vector direction where to place camera
|
|
183
|
+
encoder.uv_to_unit(unit_sphere_direction, [octahedron_u, octahedron_v]);
|
|
184
|
+
|
|
185
|
+
// offset by the radius of the sphere
|
|
186
|
+
const camera_px = distance * unit_sphere_direction[0];
|
|
187
|
+
const camera_py = distance * unit_sphere_direction[1];
|
|
188
|
+
const camera_pz = distance * unit_sphere_direction[2];
|
|
189
|
+
|
|
190
|
+
// console.log(`UV:${octahedron_u.toFixed(2)},${octahedron_v.toFixed(2)}\t V3:${unit_sphere_direction.map(n => n.toFixed(2)).join(', ')}`);
|
|
191
|
+
|
|
192
|
+
// construct projection matrix
|
|
193
|
+
cam.left = -distance;
|
|
194
|
+
cam.right = distance;
|
|
195
|
+
cam.top = distance;
|
|
196
|
+
cam.bottom = -distance;
|
|
197
|
+
cam.near = 0;
|
|
198
|
+
cam.far = distance * 2;
|
|
199
|
+
|
|
200
|
+
cam.position.set(
|
|
201
|
+
camera_px,
|
|
202
|
+
camera_py,
|
|
203
|
+
camera_pz
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
cam.lookAt(0, 0, 0);
|
|
207
|
+
cam.updateProjectionMatrix();
|
|
208
|
+
|
|
209
|
+
// update materials
|
|
210
|
+
for (let k = 0; k < bake_material_array.length; k++) {
|
|
211
|
+
const mat = bake_material_array[k];
|
|
212
|
+
|
|
213
|
+
mat.uniforms.projection_params.value.set(
|
|
214
|
+
0, 0, 0, 1 / cam.far
|
|
215
|
+
);
|
|
216
|
+
mat.uniformsNeedUpdate = true;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
//TODO consider doing super-sampling for some textures for better-looking results
|
|
220
|
+
|
|
221
|
+
/*
|
|
222
|
+
TODO it's possible to reduce bounding sphere in some cases where thin geometry would produce 0 texels
|
|
223
|
+
in the actual render, but we can't know that without doing the rendering first.
|
|
224
|
+
Consider doing a cheap pre-pass, rendering out pixels and checking if we can crop the bounding sphere further
|
|
225
|
+
*/
|
|
226
|
+
|
|
227
|
+
renderer.setViewport(i * frame_width, j * frame_height, frame_width, frame_height);
|
|
228
|
+
renderer.render(scene, cam);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/*
|
|
233
|
+
TODO dilate non-color textures to prevent artifacts when sampling around the edges
|
|
234
|
+
*/
|
|
235
|
+
|
|
236
|
+
//restore renderer state
|
|
237
|
+
renderer.setRenderTarget(_rt);
|
|
238
|
+
renderer.setViewport(_vp);
|
|
239
|
+
renderer.autoClear = _autoClear;
|
|
240
|
+
if (_enabled_antialias) {
|
|
241
|
+
gl.enable(gl.SAMPLE_ALPHA_TO_COVERAGE);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// cleanup
|
|
245
|
+
cleanup_signal.send0();
|
|
246
|
+
|
|
247
|
+
// const sampler = convertTexture2Sampler2D(rt.texture);
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
id.atlas = Sampler2D.uint8(1, resolution, resolution);
|
|
251
|
+
// id.atlas = sampler;
|
|
252
|
+
id.rt = rt;
|
|
253
|
+
|
|
254
|
+
return id;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
*
|
|
259
|
+
* @param {{mesh:ShadedGeometry, transform:mat4}[]} objects objects that should make up the impostor. Baking will be done around the origin, so make sure to position meshes accordingly to make good use of texture space.
|
|
260
|
+
* @param {number} frames how many views to capture for the atlas in each X and Y direction. Setting this to 4 will result in 16 (4*4) views captured, higher value will sacrifice detail but result in transitions being more smooth
|
|
261
|
+
* @param {number} resolution resolution of the final asset, higher = more detail
|
|
262
|
+
* @param {ImpostorCaptureType} type
|
|
263
|
+
* @returns {ImpostorDescription}
|
|
264
|
+
*/
|
|
265
|
+
bake({
|
|
266
|
+
objects,
|
|
267
|
+
frames = 12,
|
|
268
|
+
resolution = 1024,
|
|
269
|
+
type = ImpostorCaptureType.FullSphere
|
|
270
|
+
}) {
|
|
271
|
+
|
|
272
|
+
console.time('bake');
|
|
273
|
+
|
|
274
|
+
assert.defined(objects, 'object');
|
|
275
|
+
assert.isNonNegativeInteger(frames, 'frames');
|
|
276
|
+
assert.isNonNegativeInteger(resolution, 'resolution');
|
|
277
|
+
assert.greaterThanOrEqual(frames, 1, 'number of frames must be >= 1');
|
|
278
|
+
|
|
279
|
+
if (frames > resolution) {
|
|
280
|
+
throw new Error(`Frame count(=${frames}) is greater than the resolution(=${resolution}) of the bake texture, packing description is unachievable`);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const frame_resolution = resolution / frames;
|
|
284
|
+
|
|
285
|
+
if (frames >= resolution / 4) {
|
|
286
|
+
console.warn(`Bake configuration will produce less frames, this will likely result in a useless impostor that contains too little texel density to properly represent the underlying scene. [frames=${frames}, resolution=${resolution}, resulting density = ${Math.pow(frame_resolution, 2)} texels per frame]`);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (resolution % frames !== 0 && frame_resolution < 16) {
|
|
290
|
+
console.warn(`To get results at low frame resolutions number of frames should be a divisor of resolution, instead got [frames=${frames}, resolution=${resolution}, resolution/frames=${frame_resolution}]`);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (!isPowerOfTwo(resolution)) {
|
|
294
|
+
throw new Error(` 'resolution' must be a power of two, instead was '${resolution}'`);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if (frames < 2) {
|
|
298
|
+
console.warn(`Frame count is set to ${frames}, which is too low to be useful. Consider values of 2 and above. A good default is 12`);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (this._renderer === null) {
|
|
302
|
+
throw new Error('No renderer attached. Renderer is required for baking.');
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const objects_radius = compute_bounding_sphere(objects);
|
|
306
|
+
|
|
307
|
+
// we need to compute bounding sphere around origin of the object
|
|
308
|
+
let encoder;
|
|
309
|
+
if (type === ImpostorCaptureType.FullSphere) {
|
|
310
|
+
encoder = new OctahedralUvEncoder();
|
|
311
|
+
} else if (type === ImpostorCaptureType.Hemisphere) {
|
|
312
|
+
encoder = new HemiOctahedralUvEncoder();
|
|
313
|
+
} else {
|
|
314
|
+
throw new Error(`Unsupported capture mode '${type}'`);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const r = this.bake_internal({
|
|
318
|
+
resolution,
|
|
319
|
+
frames,
|
|
320
|
+
objects,
|
|
321
|
+
distance: objects_radius,
|
|
322
|
+
encoder
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
r.capture_type = type;
|
|
326
|
+
r.sphere_radius = objects_radius;
|
|
327
|
+
|
|
328
|
+
console.timeEnd('bake');
|
|
329
|
+
|
|
330
|
+
return r;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { ImpostorCaptureType } from "./ImpostorCaptureType.js";
|
|
2
|
+
|
|
3
|
+
export class ImpostorDescription {
|
|
4
|
+
/**
|
|
5
|
+
* How many XY frames should be captured
|
|
6
|
+
* More frames will result in less distortion, but lower texel density (lower resolution for each individual frame)
|
|
7
|
+
* @type {number}
|
|
8
|
+
*/
|
|
9
|
+
frame_count = 16;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* How the impostor is being captured
|
|
13
|
+
* @type {number}
|
|
14
|
+
*/
|
|
15
|
+
capture_type = ImpostorCaptureType.FullSphere;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Resolution of the texture atlas used for baking. All "frames" will be packed here
|
|
19
|
+
* Must be power of two
|
|
20
|
+
* @type {number}
|
|
21
|
+
*/
|
|
22
|
+
resolution = 1024;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Radius of baking sphere, this is centered on origin
|
|
26
|
+
* @type {number}
|
|
27
|
+
*/
|
|
28
|
+
sphere_radius = 0;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Actual baked data
|
|
32
|
+
* @type {Sampler2D|null}
|
|
33
|
+
*/
|
|
34
|
+
atlas = null;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Source scene from which impostor was baked
|
|
38
|
+
* @type {number}
|
|
39
|
+
*/
|
|
40
|
+
source_geometry_polygon_count = 0;
|
|
41
|
+
/**
|
|
42
|
+
* @type {number}
|
|
43
|
+
*/
|
|
44
|
+
source_geometry_vertex_count = 0;
|
|
45
|
+
/**
|
|
46
|
+
*
|
|
47
|
+
* @type {number}
|
|
48
|
+
*/
|
|
49
|
+
source_material_count = 0;
|
|
50
|
+
/**
|
|
51
|
+
*
|
|
52
|
+
* @type {number}
|
|
53
|
+
*/
|
|
54
|
+
source_instance_count = 0;
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* TODO: replace with something non-three.js specific
|
|
59
|
+
* @deprecated use non-three.js specific accessors instead
|
|
60
|
+
* @type {THREE.WebGLMultipleRenderTargets}
|
|
61
|
+
*/
|
|
62
|
+
rt = null;
|
|
63
|
+
}
|