@woosh/meep-engine 2.39.2 → 2.39.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/core/bvh2/{traversal/__detailed_box_volume_intersection.js → aabb3/aabb3_detailed_volume_intersection.js} +47 -27
  2. package/core/bvh2/bvh3/query/compute_tight_near_far_clipping_planes.js +13 -0
  3. package/core/bvh2/traversal/ThreeClippingPlaneComputingBVHVisitor.js +5 -3
  4. package/core/bvh2/traversal/__process_point_if_within_planes.js +1 -0
  5. package/core/bvh2/traversal/aabb3_detailed_volume_intersection_callback_based.js +60 -0
  6. package/core/geom/3d/plane/is_point_within_planes.js +46 -0
  7. package/core/geom/3d/topology/bounds/{computeTraingleClusterNormalBoundingCone.js → computeTriangleClusterNormalBoundingCone.js} +8 -1
  8. package/core/geom/Vector3.d.ts +8 -0
  9. package/editor/actions/concrete/ActionUpdateTexture.js +21 -0
  10. package/editor/actions/concrete/ArrayCopyAction.js +39 -0
  11. package/editor/actions/concrete/ModifyPatchTextureArray2DAction.js +182 -0
  12. package/editor/enableEditor.js +30 -2
  13. package/editor/tools/paint/TerrainPaintTool.js +19 -3
  14. package/editor/tools/paint/TerrainTexturePaintTool.js +19 -57
  15. package/editor/tools/paint/prototypeTerrainEditor.js +67 -0
  16. package/editor/view/ecs/ComponentControlView.js +105 -10
  17. package/editor/view/ecs/EntityEditor.js +1 -1
  18. package/engine/ecs/fow/FogOfWarSystem.js +6 -3
  19. package/engine/ecs/terrain/ecs/Terrain.js +57 -47
  20. package/engine/ecs/terrain/ecs/layers/TerrainLayer.js +16 -2
  21. package/engine/ecs/terrain/ecs/layers/TerrainLayers.js +17 -0
  22. package/engine/ecs/terrain/ecs/splat/SplatMapping.js +24 -78
  23. package/engine/ecs/terrain/ecs/splat/loadLegacyTerrainSplats.js +73 -0
  24. package/engine/graphics/camera/camera_compute_distance_to_fit_length.d.ts +1 -0
  25. package/engine/graphics/camera/camera_compute_distance_to_fit_length.js +16 -0
  26. package/engine/graphics/camera/testClippingPlaneComputation.js +7 -4
  27. package/engine/graphics/ecs/camera/Camera.js +2 -2
  28. package/engine/graphics/ecs/camera/CameraClippingPlaneComputer.js +5 -8
  29. package/engine/graphics/ecs/camera/CameraSystem.js +18 -184
  30. package/engine/graphics/ecs/camera/auto_set_camera_clipping_planes.js +32 -0
  31. package/engine/graphics/ecs/camera/build_three_camera_object.js +29 -0
  32. package/engine/graphics/ecs/camera/compute_perspective_camera_focal_position.js +27 -0
  33. package/engine/graphics/ecs/camera/frustum_from_camera.js +20 -0
  34. package/engine/graphics/ecs/camera/is_valid_distance_value.js +11 -0
  35. package/engine/graphics/ecs/camera/set_camera_aspect_ratio.js +46 -0
  36. package/engine/graphics/ecs/camera/update_camera_transform.js +17 -0
  37. package/engine/graphics/ecs/path/testPathDisplaySystem.js +19 -15
  38. package/engine/graphics/ecs/path/tube/TubePathStyle.js +1 -0
  39. package/engine/graphics/{Utils.js → makeModelView.js} +1 -10
  40. package/engine/graphics/micron/build/PatchRepresentation.js +3 -3
  41. package/engine/graphics/particles/particular/engine/emitter/ParticleEmitter.js +2 -2
  42. package/engine/graphics/render/forward_plus/LightManager.js +2 -2
  43. package/engine/graphics/render/view/CameraView.js +2 -2
  44. package/engine/graphics/texture/sampler/Sampler2D.js +1293 -1267
  45. package/engine/graphics/texture/texture_array_2d_copy.js +45 -0
  46. package/engine/graphics/util/makeMeshPreviewScene.js +2 -2
  47. package/package.json +1 -1
  48. package/view/renderModel.js +1 -1
  49. package/view/string_tag_to_css_class_name.js +12 -0
  50. package/engine/graphics/util/computeMeshPreviewCameraDistance.js +0 -10
@@ -1,9 +1,9 @@
1
- import { clamp } from "../../../core/math/clamp.js";
2
- import { inverseLerp } from "../../../core/math/inverseLerp.js";
3
1
  import { TerrainPaintTool } from "./TerrainPaintTool.js";
4
2
  import { convertSampler2D2DataURL } from "../../../engine/graphics/texture/sampler/convertSampler2D2DataURL.js";
5
3
  import { obtainTerrain } from "../../../engine/ecs/terrain/util/obtainTerrain.js";
6
- import { ModifyPatchSampler2DAction } from "../../actions/concrete/ModifyPatchSampler2DAction.js";
4
+ import { ModifyPatchTextureArray2DAction } from "../../actions/concrete/ModifyPatchTextureArray2DAction.js";
5
+ import { ArrayCopyAction } from "../../actions/concrete/ArrayCopyAction.js";
6
+ import { ActionUpdateTexture } from "../../actions/concrete/ActionUpdateTexture.js";
7
7
 
8
8
  export class TerrainTexturePaintTool extends TerrainPaintTool {
9
9
  constructor() {
@@ -68,6 +68,9 @@ export class TerrainTexturePaintTool extends TerrainPaintTool {
68
68
  */
69
69
  const layer = terrain.layers.get(this.settings.splatIndex);
70
70
 
71
+ if (layer === undefined) {
72
+ return;
73
+ }
71
74
 
72
75
  const url = convertSampler2D2DataURL(layer.diffuse);
73
76
 
@@ -82,7 +85,7 @@ export class TerrainTexturePaintTool extends TerrainPaintTool {
82
85
  super.shutdown();
83
86
  }
84
87
 
85
- paint(timeDelta) {
88
+ async paint(timeDelta) {
86
89
 
87
90
  const power = this.settings.brushStrength * timeDelta;
88
91
 
@@ -133,7 +136,6 @@ export class TerrainTexturePaintTool extends TerrainPaintTool {
133
136
  const splatWidth = splat.size.x;
134
137
  const splatHeight = splat.size.y;
135
138
 
136
- const splatLayerSize = splatWidth * splatHeight;
137
139
 
138
140
  const h_x0 = uv_x0 * splatWidth;
139
141
  const h_x1 = uv_x1 * splatWidth;
@@ -150,61 +152,21 @@ export class TerrainTexturePaintTool extends TerrainPaintTool {
150
152
  const splatIndex = this.settings.splatIndex;
151
153
 
152
154
  //create action
153
- const patchWidth = x1 - x0;
154
-
155
- const targetSplatLayerAddress = splatIndex * splatLayerSize;
156
-
157
- const splat_layer_sampler = splat.getLayerWeightSampler(splatIndex);
158
-
159
- const m_action = new ModifyPatchSampler2DAction(splat_layer_sampler, [x0, y0, x1, y1]);
160
-
161
- const patchMaterialData = m_action.patch.data;
162
-
163
-
164
- for (let y = y0; y < y1; y++) {
165
-
166
- const v = inverseLerp(h_y0, h_y1, y);
167
-
168
- for (let x = x0; x < x1; x++) {
169
-
170
- const u = inverseLerp(h_x0, h_x1, x);
171
-
172
- const markerValue = marker.sampleChannelBilinearUV(u, v, 3);
173
-
174
- if (Number.isNaN(markerValue)) {
175
- continue;
176
- }
177
155
 
178
- //write weight
179
- const splatTexelIndex = y * splatWidth + x;
180
-
181
- const baseValue = weightData[targetSplatLayerAddress + splatTexelIndex];
182
-
183
- const delta = markerValue * speed;
184
-
185
- const value = baseValue + delta;
186
-
187
- const clampedValue = clamp(value, 0, 255);
188
-
189
- if (Number.isNaN(clampedValue)) {
190
-
191
- continue;
192
- }
193
-
194
-
195
- //write new weight for the material
196
- weightData[targetSplatLayerAddress + splatTexelIndex] = clampedValue;
197
-
198
-
199
- const patchIndex = ((y - y0) * patchWidth + (x - x0));
200
-
201
- patchMaterialData[patchIndex] = clampedValue;
202
- }
203
-
204
- }
156
+ // const m_action = new ModifyPatchSampler2DAction(splat_layer_sampler, [x0, y0, x1, y1]);
157
+ const m_action = new ModifyPatchTextureArray2DAction(weightData, [splat.size.x, splat.size.y, splat.depth], [x0, y0, x1, y1]);
205
158
 
159
+ m_action.applyWeightStampToLayer(
160
+ uv_x * splat.size.x, uv_y * splat.size.y,
161
+ marker, splatIndex, speed,
162
+ 0, 255
163
+ );
206
164
 
207
- this.editor.actions.do(m_action);
165
+ await this.editor.actions.doMany([
166
+ m_action,
167
+ new ArrayCopyAction(weightData, splat.weightData),
168
+ new ActionUpdateTexture(splat.weightTexture)
169
+ ]);
208
170
  }
209
171
 
210
172
  start() {
@@ -0,0 +1,67 @@
1
+ import { EngineHarness } from "../../../engine/EngineHarness.js";
2
+ import { GizmoRenderingPlugin } from "../../../engine/graphics/render/gizmo/GizmoRenderingPlugin.js";
3
+ import { GameAssetType } from "../../../engine/asset/GameAssetType.js";
4
+ import { TextureAssetLoader } from "../../../engine/asset/loaders/texture/TextureAssetLoader.js";
5
+ import Vector2 from "../../../core/geom/Vector2.js";
6
+ import { enableEditor } from "../../enableEditor.js";
7
+ import { initializeEditor } from "../../../../model/game/initializeEditor.js";
8
+
9
+ import '../../../../../../css/game.scss';
10
+ import '../../../../../../css/editor/EntityEditorView.scss';
11
+ import '../../../../../../css/editor/EditorView.scss';
12
+ import { TerrainLayer } from "../../../engine/ecs/terrain/ecs/layers/TerrainLayer.js";
13
+ import Terrain from "../../../engine/ecs/terrain/ecs/Terrain.js";
14
+ import Vector3 from "../../../core/geom/Vector3.js";
15
+
16
+ new EngineHarness()
17
+ .initialize({
18
+ configuration(config, engine) {
19
+
20
+ config.addPlugin(GizmoRenderingPlugin);
21
+
22
+ config.addLoader(GameAssetType.Texture, new TextureAssetLoader());
23
+ }
24
+ }).then(main);
25
+
26
+ async function main(engine) {
27
+ await EngineHarness.buildBasics({
28
+ engine,
29
+ focus:new Vector3(128,0,128),
30
+ distance:30,
31
+ terrainResolution: 1,
32
+ terrainSize: new Vector2(128, 128),
33
+ enableWater: false
34
+ });
35
+
36
+ const terrain_entity = engine.entityManager.dataset.getAnyComponent(Terrain);
37
+ const terrain = terrain_entity.component;
38
+
39
+ terrain.samplerHeight.resize(256, 256);
40
+ await terrain.updateHeights();
41
+
42
+
43
+ terrain.layers.addLayer(TerrainLayer.from("data/textures/materials/terrain_township_set/512/Grass_1.png", 20, 20));
44
+ terrain.layers.addLayer(TerrainLayer.from("data/textures/materials/terrain_township_set/512/Ground_1.png", 20, 20));
45
+ terrain.layers.addLayer(TerrainLayer.from("data/textures/materials/RAYGEAS/Stylized Ground Textures/Sand_2/Sand_2_AlbedoSmoothness.png", 20, 20));
46
+ terrain.layers.buildTexture();
47
+
48
+
49
+ terrain.splat.resize(256, 256, terrain.layers.count())
50
+
51
+ await terrain.layers.loadTextureData(engine.assetManager);
52
+
53
+ terrain.splat.fillLayerWeights(0, 50);
54
+
55
+ terrain.splat.weightTexture.needsUpdate = true;
56
+ terrain.updateMaterial();
57
+
58
+ const editor_controls = enableEditor(engine, initializeEditor);
59
+
60
+ editor_controls.enable();
61
+
62
+ setTimeout(() => {
63
+ editor_controls.editor.selection.add(terrain_entity.entity);
64
+ }, 100);
65
+
66
+ console.warn(terrain);
67
+ }
@@ -5,6 +5,100 @@ import ObservedBoolean from "../../../core/model/ObservedBoolean.js";
5
5
  import LabelView from "../../../view/common/LabelView.js";
6
6
  import ButtonView from "../../../view/elements/button/ButtonView.js";
7
7
  import EmptyView from "../../../view/elements/EmptyView.js";
8
+ import { downloadAsFile } from "../../../core/binary/ByteArrayTools.js";
9
+
10
+ export async function obtainClipBoard() {
11
+ if (navigator.clipboard === undefined) {
12
+ const queries = [{
13
+ name: "clipboard-read"
14
+ }, {
15
+ name: "clipboard-write"
16
+ }];
17
+
18
+ const permission_queries = queries.map(q => navigator.permissions.query(q));
19
+
20
+ const statuses = await Promise.all(permission_queries);
21
+
22
+ for (let i = 0; i < statuses.length; i++) {
23
+ const permissionStatus = statuses[i];
24
+
25
+ if (permissionStatus.state !== "granted") {
26
+ throw new Error(`Permission for query not granted (actual state = ${permissionStatus.state}). Query:${JSON.stringify(queries[i])}`)
27
+ }
28
+ }
29
+ }
30
+
31
+ return navigator.clipboard;
32
+ }
33
+
34
+ /**
35
+ *
36
+ * @param {string} text
37
+ * @param {string} [data_name]
38
+ * @returns {Promise<void>}
39
+ */
40
+ export async function safeClipboardWriteText(text, data_name = "data") {
41
+
42
+ return obtainClipBoard().then(
43
+ () => {
44
+ return navigator.clipboard.writeText(text);
45
+ },
46
+ () => {
47
+ // no clipboard available
48
+ console.log(`No clipboard available, downloading instead`);
49
+ downloadAsFile(text, `${data_name}.json`);
50
+ }
51
+ );
52
+ }
53
+
54
+ export async function safeClipboardReadText() {
55
+
56
+ return obtainClipBoard().then(() => {
57
+
58
+ return navigator.clipboard.readText();
59
+ }, () => {
60
+ // clipboard not available
61
+ console.log(`No clipboard available, using a prompt instead`);
62
+
63
+ function clickElem(elem) {
64
+ // Thx user1601638 on Stack Overflow (6/6/2018 - https://stackoverflow.com/questions/13405129/javascript-create-and-save-file )
65
+ var eventMouse = document.createEvent("MouseEvents")
66
+ eventMouse.initMouseEvent("click", true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null)
67
+ elem.dispatchEvent(eventMouse)
68
+ }
69
+
70
+ function openFile(func) {
71
+ const fileInput = document.createElement("input");
72
+
73
+ const readFile = function (e) {
74
+ var file = e.target.files[0];
75
+ if (!file) {
76
+ return;
77
+ }
78
+ var reader = new FileReader();
79
+ reader.onload = function (e) {
80
+ var contents = e.target.result;
81
+ fileInput.func(contents)
82
+ document.body.removeChild(fileInput)
83
+ }
84
+ reader.readAsText(file)
85
+ }
86
+ fileInput.type = 'file'
87
+ fileInput.style.display = 'none'
88
+ fileInput.onchange = readFile
89
+ fileInput.func = func
90
+ document.body.appendChild(fileInput)
91
+ clickElem(fileInput)
92
+ }
93
+
94
+ return new Promise((resolve, reject) => {
95
+ openFile(resolve);
96
+ });
97
+
98
+
99
+ });
100
+
101
+ }
8
102
 
9
103
  /**
10
104
  * @template T
@@ -16,9 +110,10 @@ export class ComponentControlView extends View {
16
110
  * @param {T} component
17
111
  * @param {EntityManager} entityManager
18
112
  * @param {View} vController
113
+ * @param engine
19
114
  * @constructor
20
115
  */
21
- constructor(entity, component, entityManager, vController ) {
116
+ constructor(entity, component, entityManager, vController, engine) {
22
117
  super();
23
118
 
24
119
  this.signal = {
@@ -54,7 +149,7 @@ export class ComponentControlView extends View {
54
149
 
55
150
  const bCopy = new ButtonView({
56
151
  classList: ['copy'],
57
- action() {
152
+ async action() {
58
153
  const json = component.toJSON();
59
154
 
60
155
  const data = JSON.stringify({
@@ -62,9 +157,9 @@ export class ComponentControlView extends View {
62
157
  data: json
63
158
  }, 3, 3);
64
159
 
65
- navigator.clipboard.writeText(data).then(
66
- () => console.log(`${entity}:${typeName} copied to clip`)
67
- );
160
+ safeClipboardWriteText(data, typeName).then(() => {
161
+ console.log(`${entity}:${typeName} dumped`);
162
+ });
68
163
  }
69
164
  });
70
165
 
@@ -72,8 +167,9 @@ export class ComponentControlView extends View {
72
167
 
73
168
  const bPaste = new ButtonView({
74
169
  classList: ['paste'],
75
- action() {
76
- navigator.clipboard.readText().then(text => {
170
+ async action() {
171
+
172
+ safeClipboardReadText().then(text => {
77
173
  const json = JSON.parse(text);
78
174
 
79
175
  if (json.type !== typeName) {
@@ -81,12 +177,11 @@ export class ComponentControlView extends View {
81
177
  }
82
178
 
83
179
  //get system
84
- const system = entityManager.getOwnerSystemByComponentClass(componentType);
85
-
86
- component.fromJSON(json.data, system);
180
+ component.fromJSON(json.data, engine);
87
181
 
88
182
  console.log(`${entity}:${typeName} pasted from clip`);
89
183
  });
184
+
90
185
  }
91
186
  });
92
187
 
@@ -182,7 +182,7 @@ class EntityEditor extends View {
182
182
  });
183
183
  vBody.addChild(view);
184
184
 
185
- const controlView = new ComponentControlView(entityId, component, entityManager, vBody);
185
+ const controlView = new ComponentControlView(entityId, component, entityManager, vBody, editor.engine);
186
186
 
187
187
  const Klass = component.constructor;
188
188
 
@@ -5,7 +5,6 @@ import { Frustum } from "three";
5
5
  import { VisibilityFilter } from "../../graphics/render/visibility/VisibilityFilter.js";
6
6
  import Vector4 from "../../../core/geom/Vector4.js";
7
7
  import { Camera } from "../../graphics/ecs/camera/Camera.js";
8
- import { computePerspectiveCameraFocalPosition, frustumFromCamera } from "../../graphics/ecs/camera/CameraSystem.js";
9
8
  import WaterSystem from "../../graphics/ecs/water/WaterSystem.js";
10
9
  import Vector3 from "../../../core/geom/Vector3.js";
11
10
  import { BlendingType } from "../../graphics/texture/sampler/BlendingType.js";
@@ -13,6 +12,10 @@ import { FogOfWarRenderer } from "./shader/FogOfWarRenderer.js";
13
12
  import { StandardFrameBuffers } from "../../graphics/StandardFrameBuffers.js";
14
13
  import { CompositLayer } from "../../graphics/composit/CompositLayer.js";
15
14
  import { CompositingStages } from "../../graphics/composit/CompositingStages.js";
15
+ import {
16
+ compute_perspective_camera_focal_position
17
+ } from "../../graphics/ecs/camera/compute_perspective_camera_focal_position.js";
18
+ import { frustum_from_camera } from "../../graphics/ecs/camera/frustum_from_camera.js";
16
19
 
17
20
 
18
21
  const frustum = new Frustum();
@@ -70,7 +73,7 @@ export class FogOfWarSystem extends System {
70
73
 
71
74
  objectPredicateInitialize: (camera) => {
72
75
 
73
- computePerspectiveCameraFocalPosition(camera, cameraFocalPoint);
76
+ compute_perspective_camera_focal_position(camera, cameraFocalPoint);
74
77
 
75
78
  fog = null;
76
79
 
@@ -89,7 +92,7 @@ export class FogOfWarSystem extends System {
89
92
  dataset.traverseComponents(Camera, c => {
90
93
  if (c.active) {
91
94
 
92
- frustumFromCamera(c.object, frustum);
95
+ frustum_from_camera(c.object, frustum);
93
96
 
94
97
  const nearPlane = frustum.planes[4];
95
98
 
@@ -26,7 +26,7 @@ import { assert } from "../../../../core/assert.js";
26
26
  import { GameAssetType } from "../../../asset/GameAssetType.js";
27
27
  import { TerrainLayers } from "./layers/TerrainLayers.js";
28
28
  import { SplatMaterial } from "../../../graphics/material/SplatMaterial.js";
29
- import { loadLegacyTerrainSplats, SplatMapping } from "./splat/SplatMapping.js";
29
+ import { SplatMapping } from "./splat/SplatMapping.js";
30
30
  import { OffsetScaleTransform2D } from "./OffsetScaleTransform2D.js";
31
31
  import { GridTransformKind } from "./GridTransformKind.js";
32
32
  import { makeTerrainWorkerProxy } from "./makeTerrainWorkerProxy.js";
@@ -36,6 +36,7 @@ import { TerrainFlags } from "./TerrainFlags.js";
36
36
  import { IllegalStateException } from "../../../../core/fsm/exceptions/IllegalStateException.js";
37
37
  import { buildLightTexture } from "./BuildLightTexture.js";
38
38
  import { promiseSamplerHeight } from "./PromiseSamplerHeight.js";
39
+ import { loadLegacyTerrainSplats } from "./splat/loadLegacyTerrainSplats.js";
39
40
 
40
41
  let idCounter = 0;
41
42
 
@@ -592,48 +593,6 @@ class Terrain {
592
593
  result.set(x, y);
593
594
  }
594
595
 
595
- /**
596
- *
597
- * @param opt
598
- * @param {TerrainSystem} terrainSystem
599
- */
600
- fromJSON(opt, terrainSystem) {
601
- // mark as needing rebuilding
602
- this.clearFlag(TerrainFlags.Built);
603
-
604
- if (opt === undefined) {
605
- opt = {};
606
- }
607
-
608
- if (opt.preview !== undefined) {
609
- this.preview.fromJSON(opt.preview);
610
- }
611
-
612
- /**
613
- * Assumes lowest and highest points to be at the distance equal to half the range from 0
614
- * @type {number}
615
- */
616
- this.heightRange = opt.heightMapRange;
617
-
618
- this.resolution = opt.resolution !== undefined ? opt.resolution : 4;
619
-
620
- const size = this.size;
621
-
622
- if (opt.size !== undefined) {
623
- size.fromJSON(opt.size);
624
- }
625
-
626
- this.gridScale = opt.scale !== undefined ? opt.scale : 2;
627
- //
628
-
629
- this.heightMapURL = opt.heightMap;
630
- this.materialDesc = opt.material;
631
-
632
-
633
- // debugSamplers(this);
634
- this.build(terrainSystem.assetManager);
635
- }
636
-
637
596
  /**
638
597
  *
639
598
  * @return {Promise}
@@ -690,6 +649,15 @@ class Terrain {
690
649
  this.layers.updateLayerScales(this.size.x * this.gridScale, this.size.y * this.gridScale);
691
650
  }
692
651
 
652
+ /**
653
+ * Should be called after modifying heights to actualize changes
654
+ * @returns {Promise<void>}
655
+ */
656
+ async updateHeights() {
657
+ this.updateHeightTexture();
658
+ await this.updateWorkerHeights();
659
+ }
660
+
693
661
  updateHeightTexture() {
694
662
  const sampler = this.samplerHeight;
695
663
  const texture = this.heightTexture;
@@ -925,16 +893,58 @@ class Terrain {
925
893
  return result;
926
894
  }
927
895
 
896
+ /**
897
+ *
898
+ * @param opt
899
+ * @param {Engine} engine
900
+ */
901
+ fromJSON(opt, engine) {
902
+ // mark as needing rebuilding
903
+ this.clearFlag(TerrainFlags.Built);
904
+
905
+ if (opt === undefined) {
906
+ opt = {};
907
+ }
908
+
909
+ if (opt.preview !== undefined) {
910
+ this.preview.fromJSON(opt.preview);
911
+ }
912
+
913
+ this.resolution = opt.resolution !== undefined ? opt.resolution : 4;
914
+
915
+ const size = this.size;
916
+
917
+ if (opt.size !== undefined) {
918
+ size.fromJSON(opt.size);
919
+ }
920
+
921
+ this.gridScale = opt.scale !== undefined ? opt.scale : 2;
922
+ //
923
+
924
+ this.materialDesc = opt.material;
925
+
926
+ this.samplerHeight.fromJSON(opt.heights);
927
+ this.layers.fromJSON(opt.layers);
928
+ this.splat.fromJSON(opt.splat);
929
+
930
+
931
+ // debugSamplers(this);
932
+ this.build(engine.assetManager);
933
+ }
934
+
928
935
  toJSON() {
929
- return {
936
+ const result = {
930
937
  size: this.size.toJSON(),
931
- heightMapRange: this.heightRange,
932
938
  scale: this.gridScale,
933
939
  resolution: this.resolution,
934
- heightMap: this.heightMapURL,
935
940
  material: this.materialDesc,
936
- preview: this.preview.toJSON()
941
+ preview: this.preview.toJSON(),
942
+ heights: this.samplerHeight.toJSON(),
943
+ layers: this.layers.toJSON(),
944
+ splat: this.splat.toJSON()
937
945
  };
946
+
947
+ return result;
938
948
  }
939
949
  }
940
950
 
@@ -58,6 +58,20 @@ export class TerrainLayer {
58
58
  this.onChanged = new Signal();
59
59
  }
60
60
 
61
+ toJSON() {
62
+ return {
63
+ textureDiffuseURL: this.textureDiffuseURL,
64
+ size: this.size.toJSON(),
65
+ extra: this.extra
66
+ };
67
+ }
68
+
69
+ fromJSON({ textureDiffuseURL, size, extra = {} }) {
70
+ this.textureDiffuseURL = textureDiffuseURL;
71
+ this.size.fromJSON(size);
72
+ this.extra = extra;
73
+ }
74
+
61
75
  /**
62
76
  *
63
77
  * @param {string} url
@@ -120,9 +134,9 @@ export class TerrainLayer {
120
134
  this.diffuse = Sampler2D.uint8(destination_item_size, image.width, image.height);
121
135
  }
122
136
 
123
- const source = new Sampler2D(source_data,source_item_size,image.width, image.height);
137
+ const source = new Sampler2D(source_data, source_item_size, image.width, image.height);
124
138
 
125
- copy_Sampler2D_channel_data(source,this.diffuse);
139
+ copy_Sampler2D_channel_data(source, this.diffuse);
126
140
 
127
141
  this.onChanged.send0();
128
142
 
@@ -158,6 +158,18 @@ export class TerrainLayers {
158
158
  this.scalesTexture.generateMipmaps = false;
159
159
  }
160
160
 
161
+ toJSON() {
162
+ return {
163
+ resolution: this.resolution.toJSON(),
164
+ layers: this.layers.toJSON()
165
+ };
166
+ }
167
+
168
+ fromJSON({ resolution, layers }) {
169
+ this.resolution.fromJSON(resolution);
170
+ this.layers.fromJSON(layers, TerrainLayer);
171
+ }
172
+
161
173
  /**
162
174
  *
163
175
  * @param {number} terrainWidth
@@ -330,10 +342,15 @@ export class TerrainLayers {
330
342
 
331
343
  const arrayData = image.data;
332
344
 
345
+
333
346
  const resolution = this.resolution;
334
347
 
335
348
  const singleLayerByteSize = resolution.x * resolution.y * 3;
336
349
 
350
+ if (arrayData.length < singleLayerByteSize * index) {
351
+ throw new Error('Texture data is too small, make sure you rebuild texture data before attempting to write');
352
+ }
353
+
337
354
  const address = singleLayerByteSize * index;
338
355
 
339
356
  const sampler = this.__obtain_layer_data_at_resolution(layer, resolution);