@woosh/meep-engine 2.33.65 → 2.33.67
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/geom/2d/quad-tree/QuadTreeNode.js +3 -2
- package/engine/ecs/fow/shader/screenSpaceFogOfWarShader.js +2 -10
- package/engine/ecs/grid/NormalMap2AOMap.js +10 -13
- package/engine/ecs/terrain/ecs/BuildLightTexture.js +106 -0
- package/engine/ecs/terrain/ecs/PromiseSamplerHeight.js +59 -0
- package/engine/ecs/terrain/ecs/Terrain.js +14 -153
- package/engine/graphics/FULL_SCREEN_QUAD_VERTEX_SHADER.js +10 -0
- package/engine/graphics/filter/ImageFilter.js +6 -2
- package/engine/graphics/micron/convert_three_object_to_micron.js +14 -3
- package/engine/graphics/micron/plugin/MicronRenderPlugin.js +3 -0
- package/engine/graphics/shaders/AmbientOcclusionShader.js +57 -77
- package/engine/graphics/shaders/DenoiseShader.js +119 -0
- package/engine/graphics/texture/noise_octaves.js +33 -0
- package/engine/graphics/texture/sampler/Sampler2D2Texture.js +7 -18
- package/engine/graphics/texture/sampler/debug/paintSamplerOnHTML.js +8 -5
- package/engine/graphics/texture/sampler/sampler2DToFloat32Texture.js +10 -3
- package/generation/example/filters/SampleGroundMoistureFilter.js +2 -1
- package/generation/filtering/numeric/complex/CellFilterGaussianBlur.js +29 -13
- package/generation/grid/GridData.js +17 -2
- package/package.json +1 -1
|
@@ -315,8 +315,9 @@ export class QuadTreeNode extends AABB2 {
|
|
|
315
315
|
const x1 = this.x1;
|
|
316
316
|
const y1 = this.y1;
|
|
317
317
|
|
|
318
|
-
|
|
319
|
-
const
|
|
318
|
+
// get center point
|
|
319
|
+
const xm = (x0 + x1) * 0.5;
|
|
320
|
+
const ym = (y0 + y1) * 0.5;
|
|
320
321
|
|
|
321
322
|
for (; i < dataLength; i++) {
|
|
322
323
|
const datum = data[i];
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Matrix4, NormalBlending, ShaderMaterial, Vector2, Vector4 } from "three";
|
|
2
|
+
import { FULL_SCREEN_QUAD_VERTEX_SHADER } from "../../../graphics/FULL_SCREEN_QUAD_VERTEX_SHADER.js";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
Shader for drawing fog of war as a screen-space post-process effect
|
|
@@ -6,16 +7,7 @@ import { Matrix4, NormalBlending, ShaderMaterial, Vector2, Vector4 } from "three
|
|
|
6
7
|
*/
|
|
7
8
|
export function buildScreenSpaceFogOfWarShader() {
|
|
8
9
|
function vertexShader() {
|
|
9
|
-
return
|
|
10
|
-
varying vec2 vUv;
|
|
11
|
-
|
|
12
|
-
void main() {
|
|
13
|
-
|
|
14
|
-
vUv = uv;
|
|
15
|
-
|
|
16
|
-
gl_Position = vec4( (uv - 0.5)*2.0, 0.0, 1.0 );
|
|
17
|
-
|
|
18
|
-
}`;
|
|
10
|
+
return FULL_SCREEN_QUAD_VERTEX_SHADER;
|
|
19
11
|
}
|
|
20
12
|
|
|
21
13
|
function fragmentShader() {
|
|
@@ -5,11 +5,10 @@
|
|
|
5
5
|
|
|
6
6
|
import { ClampToEdgeWrapping, DataTexture, RGBAFormat, UnsignedByteType, Vector2 } from 'three';
|
|
7
7
|
import { Sampler2D } from '../../graphics/texture/sampler/Sampler2D.js';
|
|
8
|
-
import sampler2D2Texture from '../../graphics/texture/sampler/Sampler2D2Texture.js';
|
|
9
8
|
import AmbientOcclusionShader from '../../graphics/shaders/AmbientOcclusionShader.js';
|
|
10
|
-
import GaussianBlurShader from '../../graphics/shaders/GaussianBlurShader.js';
|
|
11
9
|
import ImageFilter from '../../graphics/filter/ImageFilter.js';
|
|
12
10
|
import { sampler2DtoFloat32Texture } from "../../graphics/texture/sampler/sampler2DToFloat32Texture.js";
|
|
11
|
+
import { DenoiseShader } from "../../graphics/shaders/DenoiseShader.js";
|
|
13
12
|
|
|
14
13
|
function filterResult2Texture(data, width, height) {
|
|
15
14
|
const result = new DataTexture();
|
|
@@ -37,6 +36,7 @@ function filterResult2Texture(data, width, height) {
|
|
|
37
36
|
* @param {Sampler2D} heightMap
|
|
38
37
|
* @param {Sampler2D} normalMap
|
|
39
38
|
* @param {Vector2} resultSize
|
|
39
|
+
* @param worldSize
|
|
40
40
|
* @param {number} [rayLength]
|
|
41
41
|
* @returns {Sampler2D}
|
|
42
42
|
*/
|
|
@@ -45,13 +45,13 @@ function normalMap2OcclusionMap(
|
|
|
45
45
|
heightMap,
|
|
46
46
|
normalMap,
|
|
47
47
|
resultSize,
|
|
48
|
+
worldSize = new Vector2(heightMap.width, heightMap.width),
|
|
48
49
|
rayLength = 17
|
|
49
50
|
) {
|
|
50
51
|
const width = resultSize.x;
|
|
51
52
|
const height = resultSize.y;
|
|
52
|
-
const resolution = new Vector2(heightMap.width, heightMap.height);
|
|
53
53
|
//
|
|
54
|
-
const normalTexture =
|
|
54
|
+
const normalTexture = sampler2DtoFloat32Texture(normalMap);
|
|
55
55
|
|
|
56
56
|
const heightTexture = sampler2DtoFloat32Texture(heightMap);
|
|
57
57
|
|
|
@@ -60,17 +60,16 @@ function normalMap2OcclusionMap(
|
|
|
60
60
|
const shaderAO = new AmbientOcclusionShader();
|
|
61
61
|
shaderAO.uniforms.heightMap.value = heightTexture;
|
|
62
62
|
shaderAO.uniforms.normalMap.value = normalTexture;
|
|
63
|
-
shaderAO.uniforms.
|
|
63
|
+
shaderAO.uniforms.world_size.value.set(worldSize.x, worldSize.y);
|
|
64
64
|
shaderAO.uniforms.rayLength.value = rayLength;
|
|
65
65
|
//perform filtering
|
|
66
66
|
const rawAO = ImageFilter(renderer, width, height, shaderAO);
|
|
67
67
|
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
shaderBlur.uniforms.sigma.value.set(1.3, 1.3);
|
|
68
|
+
const shaderDenoise = new DenoiseShader(2);
|
|
69
|
+
shaderDenoise.uniforms.source.value = filterResult2Texture(rawAO.array, width, height);
|
|
70
|
+
shaderDenoise.uniforms.pixelOffset.value.set(1 / width, 1 / height);
|
|
72
71
|
|
|
73
|
-
const smoothAO = ImageFilter(renderer, width, height,
|
|
72
|
+
const smoothAO = ImageFilter(renderer, width, height, shaderDenoise);
|
|
74
73
|
|
|
75
74
|
//create the sampler
|
|
76
75
|
const result = new Sampler2D(new Uint8ClampedArray(width * height), 1, width, height);
|
|
@@ -79,9 +78,7 @@ function normalMap2OcclusionMap(
|
|
|
79
78
|
const size = width * height;
|
|
80
79
|
|
|
81
80
|
for (let i = 0; i < size; i++) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
result.data[i] = smoothAO.array[i4];
|
|
81
|
+
result.data[i] = smoothAO.array[i * 4];
|
|
85
82
|
}
|
|
86
83
|
|
|
87
84
|
return result;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import normalMap2AOMap from "../../grid/NormalMap2AOMap.js";
|
|
2
|
+
import heightMap2NormalMap from "../../grid/HeightMap2NormalMap.js";
|
|
3
|
+
import { WebGLRendererPool } from "../../../graphics/render/RendererPool.js";
|
|
4
|
+
import Vector2 from "../../../../core/geom/Vector2.js";
|
|
5
|
+
import { writeSample2DDataToDataTexture } from "../../../graphics/texture/sampler/writeSampler2DDataToDataTexture.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
*
|
|
9
|
+
* @param {WebGLRenderer} renderer
|
|
10
|
+
* @param {Sampler2D} sampler
|
|
11
|
+
* @returns {Promise<Sampler2D>}
|
|
12
|
+
*/
|
|
13
|
+
function promiseSamplerNormal(renderer, sampler) {
|
|
14
|
+
|
|
15
|
+
console.time("generating normal map");
|
|
16
|
+
const normalSampler = heightMap2NormalMap(renderer, sampler);
|
|
17
|
+
console.timeEnd("generating normal map");
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
return Promise.resolve(normalSampler);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
*
|
|
25
|
+
* @param {WebGLRenderer} renderer
|
|
26
|
+
* @param {Promise<Sampler2D>} pSamplerNormal
|
|
27
|
+
* @param {Sampler2D} samplerHeight
|
|
28
|
+
* @param {Vector2} resolution
|
|
29
|
+
* @param {Vector2} [wordSize]
|
|
30
|
+
* @param {number} rayLength
|
|
31
|
+
* @returns {Promise<unknown>}
|
|
32
|
+
*/
|
|
33
|
+
function promiseSamplerAO(
|
|
34
|
+
{
|
|
35
|
+
renderer,
|
|
36
|
+
pSamplerNormal,
|
|
37
|
+
samplerHeight,
|
|
38
|
+
resolution,
|
|
39
|
+
wordSize = resolution,
|
|
40
|
+
rayLength
|
|
41
|
+
}
|
|
42
|
+
) {
|
|
43
|
+
return new Promise(function (resolve, reject) {
|
|
44
|
+
Promise.all([pSamplerNormal]).then(function (values) {
|
|
45
|
+
const samplerNormal = values[0];
|
|
46
|
+
console.time("generating AO map");
|
|
47
|
+
const occlusionSampler = normalMap2AOMap(
|
|
48
|
+
renderer,
|
|
49
|
+
samplerHeight,
|
|
50
|
+
samplerNormal,
|
|
51
|
+
wordSize,
|
|
52
|
+
resolution,
|
|
53
|
+
rayLength
|
|
54
|
+
);
|
|
55
|
+
console.timeEnd("generating AO map");
|
|
56
|
+
//console.info(occlusionSampler);
|
|
57
|
+
//
|
|
58
|
+
// paintSamplerOnHTML(samplerHeight, 0, 0, 40, 3);
|
|
59
|
+
// paintSamplerOnHTML(samplerNormal, 1, 0, 128, 1);
|
|
60
|
+
// paintSamplerOnHTML(occlusionSampler, 2, 0, 1, 0);
|
|
61
|
+
//
|
|
62
|
+
//
|
|
63
|
+
resolve(occlusionSampler);
|
|
64
|
+
}, reject);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
*
|
|
70
|
+
* @param {THREE.DataTexture} texture
|
|
71
|
+
* @param {Vector2} [resolution]
|
|
72
|
+
* @param {Sampler2D} heightSampler
|
|
73
|
+
* @param {number} [rayLength]
|
|
74
|
+
* @param {Vector2} [worldSize]
|
|
75
|
+
* @returns {Promise}
|
|
76
|
+
*/
|
|
77
|
+
export function buildLightTexture({
|
|
78
|
+
texture,
|
|
79
|
+
heightSampler,
|
|
80
|
+
resolution= new Vector2(texture.image.width, texture.image.height),
|
|
81
|
+
rayLength = 11,
|
|
82
|
+
worldSize = resolution
|
|
83
|
+
}) {
|
|
84
|
+
|
|
85
|
+
const renderer = WebGLRendererPool.global.get();
|
|
86
|
+
|
|
87
|
+
const normal = promiseSamplerNormal(renderer, heightSampler);
|
|
88
|
+
const ao = promiseSamplerAO({
|
|
89
|
+
renderer: renderer,
|
|
90
|
+
pSamplerNormal: normal,
|
|
91
|
+
samplerHeight: heightSampler,
|
|
92
|
+
resolution: resolution,
|
|
93
|
+
rayLength: rayLength,
|
|
94
|
+
worldSize
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const promise = ao.then(sampler => {
|
|
98
|
+
writeSample2DDataToDataTexture(sampler, texture);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
Promise.all([normal, ao]).finally(() => {
|
|
102
|
+
WebGLRendererPool.global.release(renderer);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
return promise;
|
|
106
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { BinaryBuffer } from "../../../../core/binary/BinaryBuffer.js";
|
|
2
|
+
import { deserializeTexture } from "../../../graphics/texture/sampler/TextureBinaryBufferSerializer.js";
|
|
3
|
+
import { Sampler2D } from "../../../graphics/texture/sampler/Sampler2D.js";
|
|
4
|
+
import { GameAssetType } from "../../../asset/GameAssetType.js";
|
|
5
|
+
import rgbaData2valueSampler2D from "../../../graphics/texture/sampler/rgbaData2valueSampler2D.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
*
|
|
9
|
+
* @param zRange
|
|
10
|
+
* @param heightMapURL
|
|
11
|
+
* @param {AssetManager} assetManager
|
|
12
|
+
* @returns {Promise<any>}
|
|
13
|
+
*/
|
|
14
|
+
export function promiseSamplerHeight(zRange, heightMapURL, assetManager) {
|
|
15
|
+
return new Promise(function (fulfill, reject) {
|
|
16
|
+
|
|
17
|
+
function assetObtained(asset) {
|
|
18
|
+
const array = asset.create();
|
|
19
|
+
|
|
20
|
+
const binaryBuffer = new BinaryBuffer();
|
|
21
|
+
//
|
|
22
|
+
// binaryBuffer.writeUint32(2560);
|
|
23
|
+
// binaryBuffer.writeUint32(2560);
|
|
24
|
+
//
|
|
25
|
+
// binaryBuffer.writeUint8(1);
|
|
26
|
+
//
|
|
27
|
+
// binaryBuffer.writeUint8(6);
|
|
28
|
+
|
|
29
|
+
binaryBuffer.writeBytes(new Uint8Array(array), 0, array.length);
|
|
30
|
+
|
|
31
|
+
binaryBuffer.position = 0;
|
|
32
|
+
|
|
33
|
+
const sampler2D = deserializeTexture(binaryBuffer);
|
|
34
|
+
fulfill(sampler2D);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
if (heightMapURL === undefined) {
|
|
39
|
+
console.warn('Height map is not specified');
|
|
40
|
+
const defaultSampler = new Sampler2D(new Uint8Array(1), 1, 1, 1);
|
|
41
|
+
fulfill(defaultSampler);
|
|
42
|
+
} else if (heightMapURL.endsWith('.bin')) {
|
|
43
|
+
//load texture from a binary file
|
|
44
|
+
assetManager.get(heightMapURL, GameAssetType.ArrayBuffer, assetObtained, reject);
|
|
45
|
+
} else {
|
|
46
|
+
assetManager.get(heightMapURL, GameAssetType.Image, function (asset) {
|
|
47
|
+
const image = asset.create();
|
|
48
|
+
|
|
49
|
+
// plane
|
|
50
|
+
const imgWidth = image.width;
|
|
51
|
+
const imgHeight = image.height;
|
|
52
|
+
|
|
53
|
+
const samplerHeight = rgbaData2valueSampler2D(image.data, imgWidth, imgHeight, zRange, -zRange / 2);
|
|
54
|
+
|
|
55
|
+
fulfill(samplerHeight);
|
|
56
|
+
}, reject);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
|
@@ -14,9 +14,6 @@ import {
|
|
|
14
14
|
} from 'three';
|
|
15
15
|
import { clamp } from "../../../../core/math/clamp.js";
|
|
16
16
|
import { Sampler2D } from '../../../graphics/texture/sampler/Sampler2D.js';
|
|
17
|
-
import heightMap2NormalMap from '../../grid/HeightMap2NormalMap.js';
|
|
18
|
-
import normalMap2AOMap from '../../grid/NormalMap2AOMap.js';
|
|
19
|
-
import rgbaData2valueSampler2D from '../../../graphics/texture/sampler/rgbaData2valueSampler2D.js';
|
|
20
17
|
import { BinaryNode } from '../../../../core/bvh2/BinaryNode.js';
|
|
21
18
|
import Vector2 from '../../../../core/geom/Vector2.js';
|
|
22
19
|
import { TerrainOverlay } from '../overlay/TerrainOverlay.js';
|
|
@@ -24,13 +21,9 @@ import { TerrainOverlay } from '../overlay/TerrainOverlay.js';
|
|
|
24
21
|
import Clouds from '../TerrainClouds.js';
|
|
25
22
|
|
|
26
23
|
import TerrainTileManager from '../tiles/TerrainTileManager.js';
|
|
27
|
-
import { WebGLRendererPool } from "../../../graphics/render/RendererPool.js";
|
|
28
|
-
import { deserializeTexture } from "../../../graphics/texture/sampler/TextureBinaryBufferSerializer.js";
|
|
29
|
-
import { BinaryBuffer } from "../../../../core/binary/BinaryBuffer.js";
|
|
30
24
|
import { TerrainPreview } from "../TerrainPreview.js";
|
|
31
25
|
import { assert } from "../../../../core/assert.js";
|
|
32
26
|
import { GameAssetType } from "../../../asset/GameAssetType.js";
|
|
33
|
-
import { writeSample2DDataToDataTexture } from "../../../graphics/texture/sampler/writeSampler2DDataToDataTexture.js";
|
|
34
27
|
import { TerrainLayers } from "./layers/TerrainLayers.js";
|
|
35
28
|
import { SplatMaterial } from "../../../graphics/material/SplatMaterial.js";
|
|
36
29
|
import { loadLegacyTerrainSplats, SplatMapping } from "./splat/SplatMapping.js";
|
|
@@ -41,147 +34,8 @@ import { MeepSettings } from "../../../MeepSettings.js";
|
|
|
41
34
|
import { loadLegacyTerrainLayers } from "./layers/loadLegacyTerrainLayers.js";
|
|
42
35
|
import { TerrainFlags } from "./TerrainFlags.js";
|
|
43
36
|
import { IllegalStateException } from "../../../../core/fsm/exceptions/IllegalStateException.js";
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
*
|
|
47
|
-
* @param zRange
|
|
48
|
-
* @param heightMapURL
|
|
49
|
-
* @param {AssetManager} assetManager
|
|
50
|
-
* @returns {Promise<any>}
|
|
51
|
-
*/
|
|
52
|
-
function promiseSamplerHeight(zRange, heightMapURL, assetManager) {
|
|
53
|
-
return new Promise(function (fulfill, reject) {
|
|
54
|
-
|
|
55
|
-
function assetObtained(asset) {
|
|
56
|
-
const array = asset.create();
|
|
57
|
-
|
|
58
|
-
const binaryBuffer = new BinaryBuffer();
|
|
59
|
-
//
|
|
60
|
-
// binaryBuffer.writeUint32(2560);
|
|
61
|
-
// binaryBuffer.writeUint32(2560);
|
|
62
|
-
//
|
|
63
|
-
// binaryBuffer.writeUint8(1);
|
|
64
|
-
//
|
|
65
|
-
// binaryBuffer.writeUint8(6);
|
|
66
|
-
|
|
67
|
-
binaryBuffer.writeBytes(new Uint8Array(array), 0, array.length);
|
|
68
|
-
|
|
69
|
-
binaryBuffer.position = 0;
|
|
70
|
-
|
|
71
|
-
const sampler2D = deserializeTexture(binaryBuffer);
|
|
72
|
-
fulfill(sampler2D);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
if (heightMapURL === undefined) {
|
|
77
|
-
console.warn('Height map is not specified');
|
|
78
|
-
const defaultSampler = new Sampler2D(new Uint8Array(1), 1, 1, 1);
|
|
79
|
-
fulfill(defaultSampler);
|
|
80
|
-
} else if (heightMapURL.endsWith('.bin')) {
|
|
81
|
-
//load texture from a binary file
|
|
82
|
-
assetManager.get(heightMapURL, GameAssetType.ArrayBuffer, assetObtained, reject);
|
|
83
|
-
} else {
|
|
84
|
-
assetManager.get(heightMapURL, GameAssetType.Image, function (asset) {
|
|
85
|
-
const image = asset.create();
|
|
86
|
-
|
|
87
|
-
// plane
|
|
88
|
-
const imgWidth = image.width;
|
|
89
|
-
const imgHeight = image.height;
|
|
90
|
-
|
|
91
|
-
const samplerHeight = rgbaData2valueSampler2D(image.data, imgWidth, imgHeight, zRange, -zRange / 2);
|
|
92
|
-
|
|
93
|
-
fulfill(samplerHeight);
|
|
94
|
-
}, reject);
|
|
95
|
-
}
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
*
|
|
101
|
-
* @param {WebGLRenderer} renderer
|
|
102
|
-
* @param {Sampler2D} sampler
|
|
103
|
-
* @returns {Promise<Sampler2D>}
|
|
104
|
-
*/
|
|
105
|
-
function promiseSamplerNormal(renderer, sampler) {
|
|
106
|
-
|
|
107
|
-
console.time("generating normal map");
|
|
108
|
-
const normalSampler = heightMap2NormalMap(renderer, sampler);
|
|
109
|
-
console.timeEnd("generating normal map");
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
return Promise.resolve(normalSampler);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
*
|
|
117
|
-
* @param {WebGLRenderer} renderer
|
|
118
|
-
* @param {Promise<Sampler2D>} pSamplerNormal
|
|
119
|
-
* @param {Sampler2D} samplerHeight
|
|
120
|
-
* @param {Vector2} resolution
|
|
121
|
-
* @param {number} rayLength
|
|
122
|
-
* @returns {Promise<unknown>}
|
|
123
|
-
*/
|
|
124
|
-
function promiseSamplerAO(
|
|
125
|
-
renderer,
|
|
126
|
-
pSamplerNormal,
|
|
127
|
-
samplerHeight,
|
|
128
|
-
resolution,
|
|
129
|
-
rayLength
|
|
130
|
-
) {
|
|
131
|
-
return new Promise(function (resolve, reject) {
|
|
132
|
-
Promise.all([pSamplerNormal]).then(function (values) {
|
|
133
|
-
const samplerNormal = values[0];
|
|
134
|
-
console.time("generating AO map");
|
|
135
|
-
const occlusionSampler = normalMap2AOMap(
|
|
136
|
-
renderer,
|
|
137
|
-
samplerHeight,
|
|
138
|
-
samplerNormal,
|
|
139
|
-
resolution,
|
|
140
|
-
rayLength
|
|
141
|
-
);
|
|
142
|
-
console.timeEnd("generating AO map");
|
|
143
|
-
//console.info(occlusionSampler);
|
|
144
|
-
//
|
|
145
|
-
// paintSamplerOnHTML(samplerHeight, 0, 0, 40, 3);
|
|
146
|
-
// paintSamplerOnHTML(samplerNormal, 1, 0, 128, 1);
|
|
147
|
-
// paintSamplerOnHTML(occlusionSampler, 2, 0, 1, 0);
|
|
148
|
-
//
|
|
149
|
-
//
|
|
150
|
-
resolve(occlusionSampler);
|
|
151
|
-
}, reject);
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
*
|
|
157
|
-
* @param {DataTexture} texture
|
|
158
|
-
* @param {Vector2} resolution
|
|
159
|
-
* @param {Sampler2D} heightSampler
|
|
160
|
-
* @param {number} heightRange
|
|
161
|
-
* @param {number} [rayLength]
|
|
162
|
-
* @returns {Promise}
|
|
163
|
-
*/
|
|
164
|
-
function buildLightTexture({ texture, heightSampler, resolution, rayLength = 17 }) {
|
|
165
|
-
|
|
166
|
-
const renderer = WebGLRendererPool.global.get();
|
|
167
|
-
|
|
168
|
-
if (resolution === undefined) {
|
|
169
|
-
resolution = new Vector2(texture.image.width, texture.image.height);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const normal = promiseSamplerNormal(renderer, heightSampler);
|
|
173
|
-
const ao = promiseSamplerAO(renderer, normal, heightSampler, resolution, rayLength);
|
|
174
|
-
|
|
175
|
-
const promise = ao.then(sampler => {
|
|
176
|
-
writeSample2DDataToDataTexture(sampler, texture);
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
Promise.all([normal, ao]).finally(() => {
|
|
180
|
-
WebGLRendererPool.global.release(renderer);
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
return promise;
|
|
184
|
-
}
|
|
37
|
+
import { buildLightTexture } from "./BuildLightTexture.js";
|
|
38
|
+
import { promiseSamplerHeight } from "./PromiseSamplerHeight.js";
|
|
185
39
|
|
|
186
40
|
let idCounter = 0;
|
|
187
41
|
|
|
@@ -903,13 +757,13 @@ class Terrain {
|
|
|
903
757
|
this.updateWorkerHeights();
|
|
904
758
|
}
|
|
905
759
|
|
|
906
|
-
updateWorkerHeights() {
|
|
760
|
+
async updateWorkerHeights() {
|
|
907
761
|
const s = this.samplerHeight;
|
|
908
762
|
const worker = this.buildWorker;
|
|
909
763
|
|
|
910
|
-
worker.setHeightSampler(s.data, s.itemSize, s.width, s.height);
|
|
764
|
+
await worker.setHeightSampler(s.data, s.itemSize, s.width, s.height);
|
|
911
765
|
|
|
912
|
-
|
|
766
|
+
return await worker.computeHeightRange()
|
|
913
767
|
.then(({ min, max }) => {
|
|
914
768
|
this.tiles.setHeightRange(min, max);
|
|
915
769
|
});
|
|
@@ -1012,7 +866,12 @@ class Terrain {
|
|
|
1012
866
|
this.setFlag(TerrainFlags.Built);
|
|
1013
867
|
}
|
|
1014
868
|
|
|
1015
|
-
|
|
869
|
+
/**
|
|
870
|
+
*
|
|
871
|
+
* @param {number} quality resolution in texels per grid cell (4 = 4x4 light map texels per single grid cell)
|
|
872
|
+
* @return {Promise<DataTexture>}
|
|
873
|
+
*/
|
|
874
|
+
buildLightMap(quality = 4) {
|
|
1016
875
|
|
|
1017
876
|
|
|
1018
877
|
const size_x = this.size.x;
|
|
@@ -1035,7 +894,9 @@ class Terrain {
|
|
|
1035
894
|
|
|
1036
895
|
const promise = buildLightTexture({
|
|
1037
896
|
texture,
|
|
1038
|
-
heightSampler: this.samplerHeight
|
|
897
|
+
heightSampler: this.samplerHeight,
|
|
898
|
+
worldSize: new Vector2(size_x * this.gridScale, size_y * this.gridScale),
|
|
899
|
+
resolution: new Vector2(size_x * quality, size_y *quality)
|
|
1039
900
|
});
|
|
1040
901
|
|
|
1041
902
|
|
|
@@ -5,13 +5,14 @@
|
|
|
5
5
|
|
|
6
6
|
import { LinearFilter, Mesh, OrthographicCamera, Scene, ShaderMaterial, WebGLRenderTarget } from 'three';
|
|
7
7
|
import { FULL_SCREEN_TRIANGLE_GEOMETRY } from "../geometry/FULL_SCREEN_TRIANGLE_GEOMETRY.js";
|
|
8
|
+
import { FULL_SCREEN_QUAD_VERTEX_SHADER } from "../FULL_SCREEN_QUAD_VERTEX_SHADER.js";
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
*
|
|
11
12
|
* @param {WebGLRenderer} renderer
|
|
12
13
|
* @param {number} width
|
|
13
14
|
* @param {number} height
|
|
14
|
-
* @param {{vertexShader
|
|
15
|
+
* @param {{vertexShader?:string,fragmentShader:string, uniforms?:Object, defines?:Object}} processShader
|
|
15
16
|
* @param {Uint8Array} [destination]
|
|
16
17
|
* @return {{array: Uint8Array, renderer: WebGLRenderer}}
|
|
17
18
|
*/
|
|
@@ -34,9 +35,12 @@ function processTexture(
|
|
|
34
35
|
scene.add(camera);
|
|
35
36
|
|
|
36
37
|
//make a quad to render
|
|
37
|
-
const material = new ShaderMaterial(processShader);
|
|
38
|
+
const material = new ShaderMaterial(Object.assign({ vertexShader: FULL_SCREEN_QUAD_VERTEX_SHADER }, processShader));
|
|
39
|
+
|
|
38
40
|
material.lights = false;
|
|
39
41
|
material.fog = false;
|
|
42
|
+
material.uniformsNeedUpdate = true;
|
|
43
|
+
material.needsUpdate = true;
|
|
40
44
|
|
|
41
45
|
const quad = new Mesh(FULL_SCREEN_TRIANGLE_GEOMETRY, material);
|
|
42
46
|
|
|
@@ -43,10 +43,15 @@ export async function convert_three_object_to_micron(object, micron_cache = defa
|
|
|
43
43
|
return attached_micron;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
const
|
|
46
|
+
const cached_micron = await micron_cache.get(g);
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
|
|
49
|
+
if (cached_micron !== undefined && cached_micron !== null) {
|
|
50
|
+
|
|
51
|
+
// set proxy
|
|
52
|
+
cached_micron.proxy = g;
|
|
53
|
+
|
|
54
|
+
return cached_micron;
|
|
50
55
|
}
|
|
51
56
|
|
|
52
57
|
const pending = pending_cache.get(g);
|
|
@@ -61,8 +66,14 @@ export async function convert_three_object_to_micron(object, micron_cache = defa
|
|
|
61
66
|
|
|
62
67
|
promises.push(micron_geometry_promise);
|
|
63
68
|
|
|
69
|
+
/**
|
|
70
|
+
*
|
|
71
|
+
* @type {MicronGeometry}
|
|
72
|
+
*/
|
|
64
73
|
const micron_geometry = await micron_geometry_promise;
|
|
65
74
|
|
|
75
|
+
micron_geometry.proxy = g;
|
|
76
|
+
|
|
66
77
|
await micron_cache.set(g, micron_geometry);
|
|
67
78
|
|
|
68
79
|
return micron_geometry;
|
|
@@ -106,6 +106,9 @@ class GLTFAssetTransformer extends AssetTransformer {
|
|
|
106
106
|
|
|
107
107
|
this.__serde.deserialize(buffer, micron_geometry);
|
|
108
108
|
|
|
109
|
+
// set proxy for raycasting
|
|
110
|
+
micron_geometry.proxy = geometry;
|
|
111
|
+
|
|
109
112
|
geometry[MICRON_GEOMETRY_FIELD] = micron_geometry;
|
|
110
113
|
}
|
|
111
114
|
|
|
@@ -3,9 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { Vector2 } from "three";
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
const samplesPerPixel = 37;
|
|
8
|
-
|
|
9
6
|
const AmbientOcclusionShader = function () {
|
|
10
7
|
return {
|
|
11
8
|
|
|
@@ -13,11 +10,16 @@ const AmbientOcclusionShader = function () {
|
|
|
13
10
|
|
|
14
11
|
"normalMap": { type: "t", value: null },
|
|
15
12
|
"heightMap": { type: "t", value: null },
|
|
16
|
-
"
|
|
13
|
+
"world_size": { type: "v2", value: new Vector2(512, 512) },
|
|
17
14
|
"rayLength": { type: 'f', value: 17 }
|
|
18
15
|
|
|
19
16
|
},
|
|
20
17
|
|
|
18
|
+
defines: {
|
|
19
|
+
'NUM_SAMPLES': 64,
|
|
20
|
+
'NUM_RINGS': 7,
|
|
21
|
+
},
|
|
22
|
+
|
|
21
23
|
vertexShader: [
|
|
22
24
|
|
|
23
25
|
"varying vec2 vUv;",
|
|
@@ -33,17 +35,17 @@ const AmbientOcclusionShader = function () {
|
|
|
33
35
|
|
|
34
36
|
fragmentShader: [
|
|
35
37
|
`
|
|
36
|
-
uniform vec2 resolution;
|
|
37
38
|
uniform sampler2D normalMap;
|
|
38
39
|
uniform sampler2D heightMap;
|
|
39
40
|
uniform float rayLength;
|
|
41
|
+
uniform vec2 world_size;
|
|
40
42
|
|
|
41
43
|
varying vec2 vUv;
|
|
42
44
|
|
|
43
45
|
vec3 get(float x, float y){
|
|
44
|
-
vec2 _uv = vUv.xy + vec2(x,y)/
|
|
46
|
+
vec2 _uv = vUv.xy + vec2(x,y) / world_size;
|
|
45
47
|
float h = texture2D(heightMap, _uv).x;
|
|
46
|
-
return vec3(_uv.x, h, _uv.y);
|
|
48
|
+
return vec3( _uv.x * world_size.x, h, _uv.y * world_size.y );
|
|
47
49
|
}
|
|
48
50
|
|
|
49
51
|
float hash1( float n )
|
|
@@ -56,92 +58,70 @@ const AmbientOcclusionShader = function () {
|
|
|
56
58
|
p = 50.0*fract( p*0.3183099 );
|
|
57
59
|
return fract( p.x*p.y*(p.x+p.y) );
|
|
58
60
|
}
|
|
59
|
-
|
|
60
61
|
|
|
61
|
-
|
|
62
|
-
{
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
float a = hash1(p+vec2(0,0));
|
|
68
|
-
float b = hash1(p+vec2(1,0));
|
|
69
|
-
float c = hash1(p+vec2(0,1));
|
|
70
|
-
float d = hash1(p+vec2(1,1));
|
|
71
|
-
|
|
72
|
-
return -1.0+2.0*( a + (b-a)*u.x + (c-a)*u.y + (a - b - c + d)*u.x*u.y );
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
float noise_01(in vec2 x){
|
|
76
|
-
return (noise(x) + 1.0)*0.5;
|
|
62
|
+
// Non-sin based hash function, fast and has good randomness
|
|
63
|
+
float hash12(vec2 p){
|
|
64
|
+
vec3 p3 = fract(vec3(p.xyx) * .1031);
|
|
65
|
+
p3 += dot(p3, p3.yzx + 33.33);
|
|
66
|
+
return fract((p3.x + p3.y) * p3.z);
|
|
77
67
|
}
|
|
78
68
|
|
|
79
|
-
|
|
80
|
-
float random_0 = noise_01( seed + vec2(1.123, 3.1) );
|
|
81
|
-
float random_1 = noise_01( seed + vec2(3.123, 7.8913) );
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
float angleCosine = cos(angle);
|
|
85
|
-
|
|
86
|
-
float z = random_0 * (1.0 - angleCosine) + angleCosine;
|
|
69
|
+
const float bias = 0.001;
|
|
87
70
|
|
|
88
|
-
|
|
71
|
+
float pow2(float x){
|
|
72
|
+
return x*x;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
float getOcclusion(vec3 origin, vec3 normal, vec3 hit_position){
|
|
76
|
+
vec3 viewDelta = hit_position - origin;
|
|
89
77
|
|
|
90
|
-
float
|
|
78
|
+
float viewDistance = length( viewDelta );
|
|
91
79
|
|
|
92
|
-
float
|
|
80
|
+
float vn = dot( normal, viewDelta );
|
|
81
|
+
float a2 = (vn) / viewDistance - bias;
|
|
82
|
+
float a1 = (1.0 + pow2( viewDistance ) );
|
|
93
83
|
|
|
94
|
-
|
|
95
|
-
|
|
84
|
+
return max(0.0, a2) / a1;
|
|
85
|
+
}
|
|
96
86
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
87
|
+
const float PI2 = 6.28318530717958;
|
|
88
|
+
const float kernelRadius = 100.0;
|
|
89
|
+
|
|
90
|
+
const float ANGLE_STEP = PI2 * float( NUM_RINGS ) / float( NUM_SAMPLES );
|
|
91
|
+
const float INV_NUM_SAMPLES = 1.0 / float( NUM_SAMPLES );
|
|
92
|
+
|
|
93
|
+
float getAmbientOcclusion(vec3 world_position, vec3 world_normal){
|
|
94
|
+
// jsfiddle that shows sample pattern: https://jsfiddle.net/a16ff1p7/
|
|
95
|
+
float angle = hash12( vUv ) * PI2;
|
|
96
|
+
vec2 radius = vec2( rayLength * INV_NUM_SAMPLES );
|
|
100
97
|
|
|
101
|
-
float
|
|
102
|
-
|
|
103
|
-
float c = dZ;
|
|
104
|
-
float s = sin(rot);
|
|
105
|
-
float t = 1.0 - c;
|
|
106
|
-
float tx = -t * dY;
|
|
107
|
-
float ty = t * dX;
|
|
98
|
+
float occlusionSum = 0.0;
|
|
99
|
+
float weightSum = 0.0;
|
|
108
100
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
101
|
+
for( int i = 0; i < NUM_SAMPLES; i ++ ) {
|
|
102
|
+
vec2 sampleUv = vec2( cos( angle ), sin( angle ) ) * radius * float(i+1);
|
|
103
|
+
|
|
104
|
+
angle += ANGLE_STEP;
|
|
105
|
+
|
|
106
|
+
vec3 sample_pos = get(sampleUv.x, sampleUv.y);
|
|
114
107
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
108
|
+
occlusionSum += getOcclusion(world_position, world_normal, sample_pos);
|
|
109
|
+
weightSum += 1.0;
|
|
110
|
+
}
|
|
118
111
|
|
|
119
|
-
return
|
|
112
|
+
return occlusionSum/weightSum;
|
|
120
113
|
}
|
|
121
|
-
|
|
114
|
+
|
|
122
115
|
void main() {
|
|
123
|
-
float occlusion = 0.0;
|
|
124
116
|
vec3 pos = get(0.0, 0.0);
|
|
125
|
-
vec3 normal = texture2D( normalMap, vUv ).xyz;
|
|
126
|
-
|
|
127
|
-
for(int i=1; i< ${samplesPerPixel}; i++){
|
|
128
|
-
float s = float(i)/${samplesPerPixel - 1}.0;
|
|
129
|
-
float a = sqrt(s*512.0);
|
|
130
|
-
float b = sqrt(s)*rayLength;
|
|
131
|
-
float x = sin(a)*b;
|
|
132
|
-
float y = cos(a)*b;
|
|
133
|
-
|
|
134
|
-
vec3 sample_pos = get(x, y);
|
|
135
|
-
|
|
136
|
-
vec3 sample_dir = normalize(sample_pos - pos);
|
|
137
|
-
float vDot = dot(normal, sample_dir);
|
|
138
|
-
float lambert = clamp(vDot, 0.0, 1.0);
|
|
139
|
-
float dist_factor = 0.23/sqrt(length(sample_pos - pos));
|
|
140
|
-
occlusion += dist_factor*lambert;
|
|
141
|
-
}
|
|
142
117
|
|
|
143
|
-
|
|
144
|
-
|
|
118
|
+
vec3 normal = texture2D( normalMap, vUv ).xzy;
|
|
119
|
+
|
|
120
|
+
float occlusion = getAmbientOcclusion(pos, normal);
|
|
121
|
+
|
|
122
|
+
float incident = 1.0 - occlusion;
|
|
123
|
+
|
|
124
|
+
gl_FragColor = vec4(pow(incident,10.0), 0.0, 0.0, 1.0);
|
|
145
125
|
}
|
|
146
126
|
`
|
|
147
127
|
].join('\n')
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { Vector2 } from "three";
|
|
2
|
+
|
|
3
|
+
const KERNEL_SIZE = 15;
|
|
4
|
+
|
|
5
|
+
const fragment = `
|
|
6
|
+
// bilateral filter, based on https://www.shadertoy.com/view/4dfGDH# and
|
|
7
|
+
// http://people.csail.mit.edu/sparis/bf_course/course_notes.pdf
|
|
8
|
+
// A bilateral filter is a non-linear, edge-preserving, and noise-reducing smoothing filter for images.
|
|
9
|
+
// It replaces the intensity of each pixel with a weighted average of intensity values from nearby pixels.
|
|
10
|
+
// This weight can be based on a Gaussian distribution. Crucially, the weights depend not only on
|
|
11
|
+
// Euclidean distance of pixels, but also on the radiometric differences (e.g., range differences, such
|
|
12
|
+
// as color intensity, depth distance, etc.). This preserves sharp edges.
|
|
13
|
+
|
|
14
|
+
float normpdf3(in vec3 v, in float sigma) {
|
|
15
|
+
return 0.39894 * exp(-0.5 * dot(v, v) / (sigma * sigma)) / sigma;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// filter size
|
|
19
|
+
#define MSIZE ${KERNEL_SIZE}
|
|
20
|
+
varying vec2 vUv;
|
|
21
|
+
uniform sampler2D source;
|
|
22
|
+
uniform vec2 pixelOffset;
|
|
23
|
+
uniform vec2 sigmas;
|
|
24
|
+
uniform float bZnorm;
|
|
25
|
+
uniform float kernel[MSIZE];
|
|
26
|
+
|
|
27
|
+
void main(void) {
|
|
28
|
+
|
|
29
|
+
// range sigma - controls blurriness based on a pixel distance
|
|
30
|
+
float sigma = sigmas.x;
|
|
31
|
+
|
|
32
|
+
// domain sigma - controls blurriness based on a pixel similarity (to preserve edges)
|
|
33
|
+
float bSigma = sigmas.y;
|
|
34
|
+
vec4 pixel = texture2D(source, vUv);
|
|
35
|
+
vec4 accumulatedColor = vec4(0.0);
|
|
36
|
+
float accumulatedFactor = 0.0;
|
|
37
|
+
|
|
38
|
+
// read out the texels
|
|
39
|
+
const int kSize = (MSIZE-1)/2;
|
|
40
|
+
|
|
41
|
+
for (int i = -kSize; i <= kSize; ++i) {
|
|
42
|
+
for (int j = -kSize; j <= kSize; ++j) {
|
|
43
|
+
|
|
44
|
+
// sample the pixel with offset
|
|
45
|
+
vec2 coord = vUv + vec2(float(i), float(j)) * pixelOffset;
|
|
46
|
+
vec4 rgba = texture2D(source, coord);
|
|
47
|
+
|
|
48
|
+
// bilateral factors
|
|
49
|
+
float factor = kernel[kSize + j] * kernel[kSize + i];
|
|
50
|
+
factor *= normpdf3(rgba.rgb - pixel.rgb, bSigma) * bZnorm;
|
|
51
|
+
|
|
52
|
+
// accumulate
|
|
53
|
+
accumulatedColor += factor * rgba;
|
|
54
|
+
accumulatedFactor += factor;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
gl_FragColor = accumulatedColor / accumulatedFactor;
|
|
59
|
+
}
|
|
60
|
+
`;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
*
|
|
64
|
+
* @param {number} x
|
|
65
|
+
* @param {number} sigma
|
|
66
|
+
* @return {number}
|
|
67
|
+
*/
|
|
68
|
+
function normpdf(x, sigma) {
|
|
69
|
+
return 0.39894 * Math.exp(-0.5 * x * x / (sigma * sigma)) / sigma;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
*
|
|
74
|
+
* @param {number} sigma
|
|
75
|
+
* @param {number} filterSmoothness
|
|
76
|
+
* @constructor
|
|
77
|
+
*/
|
|
78
|
+
export function DenoiseShader(sigma = 10, filterSmoothness = 0.2) {
|
|
79
|
+
// compute zNorm
|
|
80
|
+
const zNorm = 1 / normpdf(0, filterSmoothness);
|
|
81
|
+
|
|
82
|
+
// build kernel
|
|
83
|
+
const kernel = new Float32Array(KERNEL_SIZE);
|
|
84
|
+
const kSize = Math.floor((KERNEL_SIZE - 1) / 2);
|
|
85
|
+
for (let i = 0; i <= kSize; i++) {
|
|
86
|
+
const v = normpdf(i, sigma);
|
|
87
|
+
|
|
88
|
+
kernel[kSize + i] = v;
|
|
89
|
+
kernel[kSize - i] = v;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
uniforms: {
|
|
94
|
+
source: {
|
|
95
|
+
type: 't',
|
|
96
|
+
value: null
|
|
97
|
+
},
|
|
98
|
+
pixelOffset: {
|
|
99
|
+
type: 'v2',
|
|
100
|
+
/**
|
|
101
|
+
* Holds single pixel size in UV space ( 1 / texture_size )
|
|
102
|
+
*/
|
|
103
|
+
value: new Vector2(1, 1)
|
|
104
|
+
},
|
|
105
|
+
sigmas: {
|
|
106
|
+
type: "v2", value: new Vector2(sigma, sigma)
|
|
107
|
+
},
|
|
108
|
+
bZnorm: {
|
|
109
|
+
type: 'f',
|
|
110
|
+
value: zNorm
|
|
111
|
+
},
|
|
112
|
+
kernel: {
|
|
113
|
+
type: 'f',
|
|
114
|
+
value: kernel
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
fragmentShader: fragment
|
|
118
|
+
};
|
|
119
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export function noise_octaves(
|
|
2
|
+
noise,
|
|
3
|
+
noise_ctx,
|
|
4
|
+
x, y, z,
|
|
5
|
+
octaves,
|
|
6
|
+
persistence = 0.5,
|
|
7
|
+
lacunarity = 2
|
|
8
|
+
) {
|
|
9
|
+
let total = 0;
|
|
10
|
+
let frequency = 1;
|
|
11
|
+
let amplitude = 1;
|
|
12
|
+
|
|
13
|
+
let totalAmplitude = 0; // Used for normalizing result to 0.0 - 1.0
|
|
14
|
+
let i = 0;
|
|
15
|
+
|
|
16
|
+
for (; i < octaves; i++) {
|
|
17
|
+
|
|
18
|
+
const noise_value = noise.call(noise_ctx,
|
|
19
|
+
x * frequency,
|
|
20
|
+
y * frequency,
|
|
21
|
+
z * frequency
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
total += noise_value * amplitude;
|
|
25
|
+
|
|
26
|
+
totalAmplitude += amplitude;
|
|
27
|
+
amplitude *= persistence;
|
|
28
|
+
frequency *= lacunarity;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return total / totalAmplitude;
|
|
32
|
+
}
|
|
33
|
+
|
|
@@ -3,43 +3,32 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { ClampToEdgeWrapping, DataTexture, RGBAFormat, UnsignedByteType } from 'three';
|
|
5
5
|
import { assert } from "../../../../core/assert.js";
|
|
6
|
+
import { copy_Sampler2D_channel_data } from "./copy_Sampler2D_channel_data.js";
|
|
7
|
+
import { Sampler2D } from "./Sampler2D.js";
|
|
6
8
|
|
|
7
9
|
|
|
8
10
|
/**
|
|
9
11
|
*
|
|
10
12
|
* @param {Sampler2D} sampler
|
|
11
|
-
* @param {Number} scale
|
|
12
|
-
* @param {Number} offset
|
|
13
13
|
* @return {DataTexture}
|
|
14
14
|
*/
|
|
15
|
-
function sampler2D2Texture(sampler
|
|
15
|
+
function sampler2D2Texture(sampler) {
|
|
16
16
|
assert.notEqual(sampler, undefined, 'sampler is undefined');
|
|
17
|
-
assert.equal(typeof scale, "number", `scale must be a number, instead was '${typeof scale}'`);
|
|
18
|
-
assert.equal(typeof offset, "number", `scale must be a number, instead was '${typeof offset}'`);
|
|
19
17
|
|
|
20
18
|
const result = new DataTexture();
|
|
21
19
|
result.format = RGBAFormat;
|
|
22
20
|
|
|
23
21
|
let data;
|
|
24
22
|
|
|
25
|
-
if (
|
|
23
|
+
if (sampler.itemSize === 4) {
|
|
26
24
|
//no transformation, use a direct reference
|
|
27
25
|
data = sampler.data;
|
|
28
26
|
|
|
29
27
|
} else {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const arrayFiller = sampler.makeArrayFiller(scale, offset);
|
|
28
|
+
const dest = Sampler2D.uint8(4, sampler.width, sampler.height);
|
|
29
|
+
copy_Sampler2D_channel_data(sampler, dest);
|
|
33
30
|
|
|
34
|
-
|
|
35
|
-
let x, y;
|
|
36
|
-
|
|
37
|
-
for (y = 0; y < sampler.height; y++) {
|
|
38
|
-
for (x = 0; x < sampler.width; x++) {
|
|
39
|
-
arrayFiller(index, data, x, y);
|
|
40
|
-
index += 4;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
31
|
+
data = dest.data;
|
|
43
32
|
}
|
|
44
33
|
|
|
45
34
|
result.type = UnsignedByteType;
|
|
@@ -10,20 +10,23 @@ import { typedArrayConstructorByInstance } from "../typedArrayConstructorByInsta
|
|
|
10
10
|
* @param {number} y
|
|
11
11
|
* @param scale
|
|
12
12
|
* @param offset
|
|
13
|
+
* @param {number} resolution
|
|
13
14
|
*/
|
|
14
|
-
export function paintSamplerOnHTML(s, x, y, scale, offset) {
|
|
15
|
+
export function paintSamplerOnHTML(s, x, y, scale, offset, resolution=256) {
|
|
15
16
|
|
|
16
|
-
const d =
|
|
17
|
+
const d = resolution;
|
|
17
18
|
|
|
18
|
-
const
|
|
19
|
+
const TA = typedArrayConstructorByInstance(s.data);
|
|
20
|
+
|
|
21
|
+
const target = new Sampler2D(new TA(d * d * s.itemSize), s.itemSize, d, d);
|
|
19
22
|
|
|
20
23
|
scaleSampler2D(s, target);
|
|
21
24
|
|
|
22
25
|
const c = convertSampler2D2Canvas(target, scale, offset);
|
|
23
26
|
c.style.zIndex = 1000;
|
|
24
27
|
c.style.position = "absolute";
|
|
25
|
-
c.style.left = `${x
|
|
26
|
-
c.style.top = `${y
|
|
28
|
+
c.style.left = `${x}px`;
|
|
29
|
+
c.style.top = `${y}px`;
|
|
27
30
|
|
|
28
31
|
document.body.appendChild(c);
|
|
29
32
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import { ClampToEdgeWrapping, DataTexture, FloatType, LinearFilter, NearestFilter
|
|
1
|
+
import { ClampToEdgeWrapping, DataTexture, FloatType, LinearFilter, NearestFilter } from "three";
|
|
2
2
|
import { assert } from "../../../../core/assert.js";
|
|
3
|
+
import { channelCountToThreeTextureFormat } from "../channelCountToThreeTextureFormat.js";
|
|
4
|
+
import { computeThreeTextureInternalFormatFromDataType } from "../../render/forward_plus/data/computeThreeTextureInternalFormatFromDataType.js";
|
|
5
|
+
import { DataType } from "../../../../core/collection/table/DataType.js";
|
|
3
6
|
|
|
4
7
|
/**
|
|
5
8
|
*
|
|
@@ -12,7 +15,7 @@ export function sampler2DtoFloat32Texture(sampler) {
|
|
|
12
15
|
const width = sampler.width;
|
|
13
16
|
const height = sampler.height;
|
|
14
17
|
|
|
15
|
-
const texture = new DataTexture(sampler.data, width, height
|
|
18
|
+
const texture = new DataTexture(sampler.data, width, height);
|
|
16
19
|
|
|
17
20
|
texture.wrapS = ClampToEdgeWrapping;
|
|
18
21
|
texture.wrapT = ClampToEdgeWrapping;
|
|
@@ -24,7 +27,11 @@ export function sampler2DtoFloat32Texture(sampler) {
|
|
|
24
27
|
|
|
25
28
|
texture.flipY = false;
|
|
26
29
|
|
|
27
|
-
texture.
|
|
30
|
+
texture.type = FloatType;
|
|
31
|
+
texture.format = channelCountToThreeTextureFormat(sampler.itemSize);
|
|
32
|
+
texture.internalFormat = computeThreeTextureInternalFormatFromDataType(DataType.Float32, sampler.itemSize);
|
|
33
|
+
|
|
34
|
+
texture.needsUpdate = true;
|
|
28
35
|
|
|
29
36
|
return texture;
|
|
30
37
|
}
|
|
@@ -1,9 +1,23 @@
|
|
|
1
1
|
import { CellFilter } from "../../CellFilter.js";
|
|
2
2
|
import { gaussian } from "../../../../core/math/gaussian.js";
|
|
3
|
-
|
|
3
|
+
import { max2 } from "../../../../core/math/max2.js";
|
|
4
|
+
import { assert } from "../../../../core/assert.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
*
|
|
8
|
+
* @param {number[]} result
|
|
9
|
+
* @param {number} samplesX
|
|
10
|
+
* @param {number} samplesY
|
|
11
|
+
* @param {number} sigma_x
|
|
12
|
+
* @param {number} sigma_y
|
|
13
|
+
* @return {number}
|
|
14
|
+
*/
|
|
4
15
|
function buildKernel(result, samplesX, samplesY, sigma_x, sigma_y) {
|
|
5
|
-
|
|
6
|
-
|
|
16
|
+
assert.isNonNegativeInteger(samplesX, 'samplesX');
|
|
17
|
+
assert.isNonNegativeInteger(samplesY, 'samplesY');
|
|
18
|
+
|
|
19
|
+
const half_samples_x = samplesX * 0.5;
|
|
20
|
+
const half_samples_y = samplesY * 0.5;
|
|
7
21
|
|
|
8
22
|
let powerTotal = 0;
|
|
9
23
|
let ix, iy;
|
|
@@ -70,6 +84,8 @@ export class CellFilterGaussianBlur extends CellFilter {
|
|
|
70
84
|
if (!this.source.initialized) {
|
|
71
85
|
this.source.initialize(grid, seed);
|
|
72
86
|
|
|
87
|
+
this.__kernel = new Float32Array(this.samples_x * this.samples_y);
|
|
88
|
+
|
|
73
89
|
// initialize kernel
|
|
74
90
|
this.__kernel_total_power = buildKernel(this.__kernel, this.samples_x, this.samples_y, this.sigma_x, this.sigma_y);
|
|
75
91
|
}
|
|
@@ -84,13 +100,13 @@ export class CellFilterGaussianBlur extends CellFilter {
|
|
|
84
100
|
* @param {number} quality
|
|
85
101
|
* @returns {CellFilterGaussianBlur}
|
|
86
102
|
*/
|
|
87
|
-
static from(source, x, y, quality =
|
|
103
|
+
static from(source, x, y, quality = 3) {
|
|
88
104
|
const r = new CellFilterGaussianBlur();
|
|
89
105
|
|
|
90
106
|
r.source = source;
|
|
91
107
|
|
|
92
|
-
r.samples_x = quality * x;
|
|
93
|
-
r.samples_y = quality * y;
|
|
108
|
+
r.samples_x = max2(2, Math.round(quality * x));
|
|
109
|
+
r.samples_y = max2(2, Math.round(quality * y));
|
|
94
110
|
|
|
95
111
|
r.size_x = x;
|
|
96
112
|
r.size_y = y;
|
|
@@ -102,24 +118,24 @@ export class CellFilterGaussianBlur extends CellFilter {
|
|
|
102
118
|
const samplesX = this.samples_x;
|
|
103
119
|
const samplesY = this.samples_y;
|
|
104
120
|
|
|
105
|
-
const half_samples_x = (samplesX
|
|
106
|
-
const half_samples_y = (samplesY
|
|
121
|
+
const half_samples_x = (samplesX - 1) * 0.5;
|
|
122
|
+
const half_samples_y = (samplesY - 1) * 0.5;
|
|
107
123
|
|
|
108
124
|
const sizeX = this.size_x;
|
|
109
125
|
const sizeY = this.size_y;
|
|
110
126
|
|
|
111
|
-
const local_u_scale = 1 / samplesX;
|
|
112
|
-
const local_v_scale = 1 / samplesY;
|
|
127
|
+
const local_u_scale = 1 / (samplesX - 1);
|
|
128
|
+
const local_v_scale = 1 / (samplesY - 1);
|
|
113
129
|
|
|
114
130
|
let sum = 0;
|
|
115
|
-
let ix, iy;
|
|
131
|
+
let ix, iy = 0;
|
|
116
132
|
|
|
117
133
|
const source = this.source;
|
|
118
134
|
const kernel = this.__kernel;
|
|
119
135
|
|
|
120
|
-
const i_samples_x = samplesX|0;
|
|
136
|
+
const i_samples_x = samplesX | 0;
|
|
121
137
|
|
|
122
|
-
for (
|
|
138
|
+
for (; iy < samplesY; iy++) {
|
|
123
139
|
const local_y = iy - half_samples_y;
|
|
124
140
|
|
|
125
141
|
const ny = local_y * local_v_scale;
|
|
@@ -2,6 +2,7 @@ import { assert } from "../../core/assert.js";
|
|
|
2
2
|
import { QuadTreeNode } from "../../core/geom/2d/quad-tree/QuadTreeNode.js";
|
|
3
3
|
import { OffsetScaleTransform2D } from "../../engine/ecs/terrain/ecs/OffsetScaleTransform2D.js";
|
|
4
4
|
import { MarkerMatchCounter } from "./MarkerMatchCounter.js";
|
|
5
|
+
import { max2 } from "../../core/math/max2.js";
|
|
5
6
|
|
|
6
7
|
const scratch_match_counter = new MarkerMatchCounter();
|
|
7
8
|
|
|
@@ -167,8 +168,14 @@ export class GridData {
|
|
|
167
168
|
* @param {number} height
|
|
168
169
|
*/
|
|
169
170
|
resize(width, height) {
|
|
170
|
-
assert.
|
|
171
|
-
assert.
|
|
171
|
+
assert.isNumber(width, 'width');
|
|
172
|
+
assert.isNumber(height, 'height');
|
|
173
|
+
|
|
174
|
+
assert.isNonNegativeInteger(width,'width');
|
|
175
|
+
assert.isNonNegativeInteger(height,'height');
|
|
176
|
+
|
|
177
|
+
assert.isFiniteNumber(width, 'width');
|
|
178
|
+
assert.isFiniteNumber(height, 'width');
|
|
172
179
|
|
|
173
180
|
if (width === this.width && height === this.height) {
|
|
174
181
|
//no need, already the right size
|
|
@@ -187,6 +194,14 @@ export class GridData {
|
|
|
187
194
|
|
|
188
195
|
layer.resize(width, height);
|
|
189
196
|
}
|
|
197
|
+
|
|
198
|
+
// resize the quad tree pre-emptively, to avoid expensive re-balancing operations later on
|
|
199
|
+
this.markers.resize(
|
|
200
|
+
-max2(1, width * 0.5),
|
|
201
|
+
-max2(1, height * 0.5),
|
|
202
|
+
max2(width + 1, width * 1.5),
|
|
203
|
+
max2(height + 1, height * 1.5)
|
|
204
|
+
);
|
|
190
205
|
}
|
|
191
206
|
|
|
192
207
|
/**
|
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"productName": "Meep",
|
|
6
6
|
"description": "production-ready JavaScript game engine based on Entity Component System Architecture",
|
|
7
7
|
"author": "Alexander Goldring",
|
|
8
|
-
"version": "2.33.
|
|
8
|
+
"version": "2.33.67",
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"gl-matrix": "3.4.3",
|
|
11
11
|
"fast-levenshtein": "2.0.6",
|