66import com .jme3 .scene .Node ;
77import com .jme3 .scene .Spatial ;
88import com .jme3 .terrain .Terrain ;
9+ import com .ss .editor .annotation .FromAnyThread ;
910import com .ss .editor .annotation .JmeThread ;
1011import com .ss .editor .model .undo .editor .ChangeConsumer ;
1112import com .ss .editor .model .undo .editor .ModelChangeConsumer ;
1213import com .ss .editor .ui .component .painting .terrain .TerrainPaintingComponent ;
1314import com .ss .editor .ui .control .property .operation .PropertyOperation ;
15+ import com .ss .editor .util .NodeUtils ;
16+ import com .ss .rlib .util .array .Array ;
17+ import com .ss .rlib .util .array .ArrayFactory ;
18+ import com .ss .rlib .util .dictionary .Dictionary ;
1419import com .ss .rlib .util .dictionary .DictionaryFactory ;
1520import com .ss .rlib .util .dictionary .ObjectDictionary ;
1621import org .jetbrains .annotations .NotNull ;
17- import org .jetbrains .annotations .Nullable ;
1822
1923import java .util .ArrayList ;
2024import java .util .List ;
25+ import java .util .function .Supplier ;
2126
2227/**
2328 * The base implementation of tool control to change height of terrain.
2631 */
2732public class ChangeHeightTerrainToolControl extends TerrainToolControl {
2833
34+ @ NotNull
35+ private static final Supplier <ObjectDictionary <HeightPoint , Float >> DICTIONARY_FACTORY = () ->
36+ DictionaryFactory .newObjectDictionary (0.2F , 10000 );
37+
2938 private static class HeightPoint {
3039
3140 private final float x ;
@@ -63,17 +72,25 @@ public int hashCode() {
6372 * The table of original heights.
6473 */
6574 @ NotNull
66- private final ObjectDictionary <HeightPoint , Float > originalHeight ;
75+ private final ObjectDictionary <Terrain , ObjectDictionary <HeightPoint , Float >> originalHeight ;
76+
77+ /**
78+ * The table with copied of original terrains.
79+ */
80+ @ NotNull
81+ private final ObjectDictionary <Terrain , Spatial > copiedTerrains ;
6782
6883 /**
69- * The copy of an original terrain .
84+ * The current terrains .
7085 */
71- @ Nullable
72- private Spatial copiedTerrain ;
86+ @ NotNull
87+ private final Array < Terrain > terrains ;
7388
7489 public ChangeHeightTerrainToolControl (@ NotNull final TerrainPaintingComponent component ) {
7590 super (component );
76- this .originalHeight = DictionaryFactory .newObjectDictionary (0.2F , 1000 );
91+ this .originalHeight = DictionaryFactory .newObjectDictionary ();
92+ this .copiedTerrains = DictionaryFactory .newObjectDictionary ();
93+ this .terrains = ArrayFactory .newArray (Terrain .class );
7794 }
7895
7996 /**
@@ -82,32 +99,74 @@ public ChangeHeightTerrainToolControl(@NotNull final TerrainPaintingComponent co
8299 * @return the table of original heights.
83100 */
84101 @ JmeThread
85- private @ NotNull ObjectDictionary <HeightPoint , Float > getOriginalHeight () {
102+ private @ NotNull ObjectDictionary <Terrain , ObjectDictionary < HeightPoint , Float > > getOriginalHeight () {
86103 return originalHeight ;
87104 }
88105
106+ /**
107+ * Get the table with copied of original terrains.
108+ *
109+ * @return the table with copied of original terrains.
110+ */
111+ @ JmeThread
112+ private @ NotNull ObjectDictionary <Terrain , Spatial > getCopiedTerrains () {
113+ return copiedTerrains ;
114+ }
115+
116+ /**
117+ * Get the current terrains.
118+ *
119+ * @return the current terrains.
120+ */
121+ @ JmeThread
122+ protected @ NotNull Array <Terrain > getTerrains () {
123+ return terrains ;
124+ }
125+
89126 /**
90127 * Start making changes.
91128 */
92129 @ JmeThread
93130 protected void startChange () {
94131
95- final ObjectDictionary <HeightPoint , Float > originalHeight = getOriginalHeight ();
132+ final ObjectDictionary <Terrain , ObjectDictionary <HeightPoint , Float >> originalHeight = getOriginalHeight ();
133+ originalHeight .forEach (Dictionary ::clear );
96134 originalHeight .clear ();
97135
98- copiedTerrain = notNull (getPaintedModel ()).clone ();
136+ final Array <Terrain > terrains = getTerrains ();
137+ terrains .clear ();
138+
139+ NodeUtils .visitSpatial (notNull (getPaintedModel ()), spatial -> {
140+ if (spatial instanceof Terrain ) {
141+ terrains .add ((Terrain ) spatial );
142+ return false ;
143+ }
144+ return true ;
145+ });
146+
147+ final ObjectDictionary <Terrain , Spatial > copiedTerrains = getCopiedTerrains ();
148+ copiedTerrains .clear ();
149+
150+ terrains .forEach (copiedTerrains , (terrain , toStore ) ->
151+ toStore .put (terrain , ((Spatial ) terrain ).clone ()));
152+ }
153+
154+ @ JmeThread
155+ @ Deprecated
156+ protected void change (@ NotNull final Vector2f point ) {
157+ change (null , point );
99158 }
100159
101160 /**
102- * Notify about wanting to change height of a point.
161+ * Notify about changing height by the point in the terrain .
103162 *
104- * @param point the point.
163+ * @param terrain the terrain.
164+ * @param point the point.
105165 */
106166 @ JmeThread
107- protected void change (@ NotNull final Vector2f point ) {
167+ protected void change (@ NotNull final Terrain terrain , @ NotNull final Vector2f point ) {
108168
109- final Terrain terrain = (Terrain ) notNull (copiedTerrain );
110- final Node terrainNode = (Node ) notNull (getPaintedModel ());
169+ final Node terrainNode = (Node ) terrain ;
111170 final Vector3f scale = terrainNode .getWorldScale ();
112171
113172 final int halfSize = terrain .getTerrainSize () / 2 ;
@@ -116,54 +175,80 @@ protected void change(@NotNull final Vector2f point) {
116175
117176 final HeightPoint heightPoint = new HeightPoint (point .getX (), point .getY (), x , z );
118177
119- final ObjectDictionary <HeightPoint , Float > originalHeight = getOriginalHeight ();
120- if (originalHeight .containsKey (heightPoint )) {
178+ final ObjectDictionary <Terrain , ObjectDictionary <HeightPoint , Float >> originalHeight = getOriginalHeight ();
179+ final ObjectDictionary <HeightPoint , Float > terrainHeights = originalHeight .get (terrain , DICTIONARY_FACTORY );
180+ if (terrainHeights .containsKey (heightPoint )) {
121181 return ;
122182 }
123183
124184 final float height = terrain .getHeightmapHeight (point );
125-
126- originalHeight .put (heightPoint , height );
185+ terrainHeights .put (heightPoint , height );
127186 }
128187
129188 /**
130189 * Commit all changes.
131190 */
132191 protected void commitChanges () {
133192
134- final Terrain terrain = ( Terrain ) notNull ( getPaintedModel () );
135- final ObjectDictionary <Vector2f , Float > oldValues = DictionaryFactory .newObjectDictionary ();
136- final ObjectDictionary <Vector2f , Float > newValues = DictionaryFactory .newObjectDictionary ();
193+ final Spatial paintedModel = getPaintedModel ();
194+ final ObjectDictionary <Terrain , ObjectDictionary < Vector2f , Float > > oldValues = DictionaryFactory .newObjectDictionary ();
195+ final ObjectDictionary <Terrain , ObjectDictionary < Vector2f , Float > > newValues = DictionaryFactory .newObjectDictionary ();
137196
138- final ObjectDictionary <HeightPoint , Float > originalHeight = getOriginalHeight ();
139- originalHeight .forEach ((heightPoint , height ) -> oldValues .put (new Vector2f (heightPoint .x , heightPoint .y ), height ));
140- originalHeight .forEach ((heightPoint , value ) -> {
141- final Vector2f point = new Vector2f (heightPoint .x , heightPoint .y );
142- newValues .put (point , terrain .getHeightmapHeight (point ));
197+ final ObjectDictionary <Terrain , ObjectDictionary <HeightPoint , Float >> originalHeight = getOriginalHeight ();
198+ originalHeight .forEach ((terrain , floats ) -> {
199+ final ObjectDictionary <Vector2f , Float > values = oldValues .get (terrain , () -> createValuesDictionary (floats ));
200+ floats .forEach ((heightPoint , height ) -> values .put (new Vector2f (heightPoint .x , heightPoint .y ), height ));
143201 });
144202
145- final PropertyOperation <ChangeConsumer , Terrain , ObjectDictionary <Vector2f , Float >> operation =
146- new PropertyOperation <>(terrain , "Heightmap" , newValues , oldValues );
203+ originalHeight .forEach ((terrain , floats ) -> {
204+ final ObjectDictionary <Vector2f , Float > values = oldValues .get (terrain , () -> createValuesDictionary (floats ));
205+ floats .forEach ((heightPoint , height ) -> {
206+ final Vector2f point = new Vector2f (heightPoint .x , heightPoint .y );
207+ values .put (point , terrain .getHeightmapHeight (point ));
208+ });
209+ });
147210
148- operation .setApplyHandler ((toChange , heightMap ) -> {
211+ final Array <Terrain > toApply = ArrayFactory .newArray (Terrain .class );
212+ toApply .addAll (getTerrains ());
149213
150- final List <Vector2f > points = new ArrayList <>( heightMap . size ());
151- final List < Float > heights = new ArrayList <>(heightMap . size () );
214+ final PropertyOperation < ChangeConsumer , Spatial , ObjectDictionary < Terrain , ObjectDictionary <Vector2f , Float >>> operation =
215+ new PropertyOperation <>(paintedModel , "Heightmap" , newValues , oldValues );
152216
153- heightMap .forEach ((point , height ) -> {
154- points .add (point );
155- heights .add (height );
156- });
217+ operation .setApplyHandler ((node , heightMaps ) -> {
218+
219+ for (final Terrain terrain : toApply ) {
220+
221+ final ObjectDictionary <Vector2f , Float > heightData = heightMaps .get (terrain );
222+ if (heightData == null || heightData .isEmpty ()) {
223+ continue ;
224+ }
225+
226+ final List <Vector2f > points = new ArrayList <>(heightMaps .size ());
227+ final List <Float > heights = new ArrayList <>(heightMaps .size ());
157228
158- toChange .setHeight (points , heights );
159- ((Node ) toChange ).updateModelBound ();
229+ heightData .forEach ((point , height ) -> {
230+ points .add (point );
231+ heights .add (height );
232+ });
233+
234+ terrain .setHeight (points , heights );
235+ }
236+
237+ node .updateModelBound ();
160238 });
161239
240+ originalHeight .forEach (Dictionary ::clear );
162241 originalHeight .clear ();
163242
164243 final ModelChangeConsumer changeConsumer = getChangeConsumer ();
165244 changeConsumer .execute (operation );
166245
167- copiedTerrain = null ;
246+ getTerrains ().clear ();
247+ getCopiedTerrains ().clear ();
248+ }
249+
250+ @ FromAnyThread
251+ private @ NotNull ObjectDictionary <Vector2f , Float > createValuesDictionary (@ NotNull final ObjectDictionary <HeightPoint , Float > floats ) {
252+ return DictionaryFactory .newObjectDictionary (0.2F , floats .size ());
168253 }
169254}
0 commit comments