Skip to content

Commit 2a3d582

Browse files
committed
relink deserialized AnimEvents from the cinematic appstate
1 parent 36881e7 commit 2a3d582

File tree

2 files changed

+126
-71
lines changed

2 files changed

+126
-71
lines changed

jme3-core/src/main/java/com/jme3/cinematic/Cinematic.java

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,20 +31,25 @@
3131
*/
3232
package com.jme3.cinematic;
3333

34+
import com.jme3.anim.AnimComposer;
3435
import com.jme3.animation.LoopMode;
3536
import com.jme3.app.Application;
3637
import com.jme3.app.state.AppState;
3738
import com.jme3.app.state.AppStateManager;
3839
import com.jme3.cinematic.events.AbstractCinematicEvent;
40+
import com.jme3.cinematic.events.AnimEvent;
3941
import com.jme3.cinematic.events.CameraEvent;
4042
import com.jme3.cinematic.events.CinematicEvent;
4143
import com.jme3.export.*;
4244
import com.jme3.renderer.Camera;
4345
import com.jme3.renderer.RenderManager;
4446
import com.jme3.scene.CameraNode;
4547
import com.jme3.scene.Node;
48+
import com.jme3.scene.Spatial;
4649
import com.jme3.scene.control.CameraControl;
4750
import com.jme3.scene.control.CameraControl.ControlDirection;
51+
import com.jme3.util.clone.Cloner;
52+
4853
import java.io.IOException;
4954
import java.util.ArrayList;
5055
import java.util.HashMap;
@@ -92,6 +97,7 @@
9297
public class Cinematic extends AbstractCinematicEvent implements AppState {
9398

9499
private static final Logger logger = Logger.getLogger(Cinematic.class.getName());
100+
private static final String CINEMATIC_REF = "Cinematic:Refs";
95101

96102
private Application app;
97103
private Node scene;
@@ -241,7 +247,29 @@ public void onPause() {
241247
public void write(JmeExporter ex) throws IOException {
242248
super.write(ex);
243249
OutputCapsule oc = ex.getCapsule(this);
244-
oc.write(cinematicEvents.toArray(new CinematicEvent[cinematicEvents.size()]), "cinematicEvents", null);
250+
CinematicEvent[] events = new CinematicEvent[cinematicEvents.size()];
251+
for (int i = 0; i < cinematicEvents.size(); i++) {
252+
CinematicEvent ce = cinematicEvents.get(i);
253+
if (ce instanceof AnimEvent) {
254+
AnimEvent animEvent = (AnimEvent) ce;
255+
256+
// set ref id that will be used to relink the composer after deserialization
257+
String refId = animEvent.getAnimRef();
258+
AnimComposer composer = animEvent.getComposer();
259+
setModelRefId(composer.getSpatial(), refId);
260+
261+
// HACK: create a clone of the event without the composer
262+
// this is used to make this appstate deserializable without
263+
// breaking the scene graph
264+
Cloner cloner = new Cloner();
265+
animEvent = (AnimEvent) cloner.clone(animEvent);
266+
animEvent.setComposer(null);
267+
ce = animEvent;
268+
269+
}
270+
events[i] = ce;
271+
}
272+
oc.write(events, "cinematicEvents", null);
245273
oc.writeStringSavableMap(cameras, "cameras", null);
246274
oc.write(timeLine, "timeLine", null);
247275
}
@@ -260,7 +288,6 @@ public void read(JmeImporter im) throws IOException {
260288

261289
Savable[] events = ic.readSavableArray("cinematicEvents", null);
262290
for (Savable c : events) {
263-
// addCinematicEvent(((CinematicEvent) c).getTime(), (CinematicEvent) c)
264291
cinematicEvents.add((CinematicEvent) c);
265292
}
266293
cameras = (Map<String, CameraNode>) ic.readStringSavableMap("cameras", null);
@@ -294,9 +321,23 @@ public void setSpeed(float speed) {
294321
public void initialize(AppStateManager stateManager, Application app) {
295322
this.app = app;
296323
initEvent(app, this);
324+
297325
for (CinematicEvent cinematicEvent : cinematicEvents) {
326+
if (cinematicEvent instanceof AnimEvent) {
327+
AnimEvent animEvent = (AnimEvent) cinematicEvent;
328+
AnimComposer composer = animEvent.getComposer();
329+
if (composer == null) {
330+
String ref = animEvent.getAnimRef();
331+
Spatial sp = findModelByRef(scene, ref);
332+
if (sp != null) {
333+
composer = sp.getControl(AnimComposer.class);
334+
animEvent.setComposer(composer);
335+
}
336+
}
337+
}
298338
cinematicEvent.initEvent(app, this);
299339
}
340+
300341
if (!cameras.isEmpty()) {
301342
for (CameraNode n : cameras.values()) {
302343
n.setCamera(app.getCamera());
@@ -305,6 +346,42 @@ public void initialize(AppStateManager stateManager, Application app) {
305346
initialized = true;
306347
}
307348

349+
@SuppressWarnings("unchecked")
350+
private Spatial findModelByRef(Spatial sp, String spatialRef) {
351+
Object refIdsObj = sp.getUserData(CINEMATIC_REF);
352+
if ((refIdsObj instanceof List)) {
353+
List<String> refIds = (List<String>) refIdsObj;
354+
for (String refId : refIds) {
355+
if (spatialRef.equals(refId)) {
356+
return sp;
357+
}
358+
}
359+
}
360+
if (sp instanceof Node) {
361+
for (Spatial child : ((Node) sp).getChildren()) {
362+
Spatial model = findModelByRef(child, spatialRef);
363+
if (model != null) {
364+
return model;
365+
}
366+
}
367+
}
368+
return null;
369+
}
370+
371+
@SuppressWarnings("unchecked")
372+
private void setModelRefId(Spatial sp, String spatialRef) {
373+
Object refIdsObj = sp.getUserData(CINEMATIC_REF);
374+
List<String> refIds;
375+
if (refIdsObj instanceof List) {
376+
refIds = (List<String>) refIdsObj;
377+
if (refIds.contains(spatialRef)) return;
378+
} else {
379+
refIds = new ArrayList<>();
380+
sp.setUserData(CINEMATIC_REF, refIds);
381+
}
382+
refIds.add(spatialRef);
383+
}
384+
308385
/**
309386
* used internally
310387
*

jme3-core/src/main/java/com/jme3/cinematic/events/AnimEvent.java

Lines changed: 47 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@
4141
import com.jme3.export.JmeExporter;
4242
import com.jme3.export.JmeImporter;
4343
import com.jme3.export.OutputCapsule;
44-
import com.jme3.scene.Node;
45-
import com.jme3.scene.Spatial;
44+
import com.jme3.util.clone.Cloner;
45+
import com.jme3.util.clone.JmeCloneable;
4646

4747
import java.io.IOException;
4848
import java.util.concurrent.atomic.AtomicLong;
@@ -55,19 +55,18 @@
5555
*
5656
* Inspired by Nehon's {@link AnimationEvent}.
5757
*/
58-
public class AnimEvent extends AbstractCinematicEvent {
58+
public class AnimEvent extends AbstractCinematicEvent implements JmeCloneable {
5959

6060
private static final Logger logger
6161
= Logger.getLogger(AnimEvent.class.getName());
62-
63-
private static final String CINEMATIC_REF = "Cinematic:Refs";
6462

65-
private static final AtomicLong spatialId = new AtomicLong();
63+
64+
private static final AtomicLong refCounter = new AtomicLong();
6665

6766
/**
68-
* Reference ID used to find the associated Spatial.
67+
* Unique-ish id that identify this anim event
6968
*/
70-
protected String spatialRef;
69+
protected String animRef;
7170
/*
7271
* Control that will play the animation
7372
*/
@@ -102,6 +101,7 @@ public AnimEvent(AnimComposer composer, String actionName) {
102101
*/
103102
public AnimEvent(AnimComposer composer, String actionName,
104103
String layerName) {
104+
this();
105105
this.composer = composer;
106106
this.actionName = actionName;
107107
this.layerName = layerName;
@@ -110,50 +110,14 @@ public AnimEvent(AnimComposer composer, String actionName,
110110
*/
111111
Action eventAction = composer.action(actionName);
112112
initialDuration = (float) eventAction.getLength();
113-
114-
spatialRef = generateSpatialRef();
115-
composer.getSpatial().setUserData(CINEMATIC_REF, spatialRef);
116113
}
117114

118115
/**
119116
* No-argument constructor needed by SavableClassUtil.
120117
*/
121118
protected AnimEvent() {
122119
super();
123-
}
124-
125-
/**
126-
* Generate a unique identifier used to tag a Spatial in the scene graph.
127-
*
128-
* @return a unique string identifier
129-
*/
130-
private String generateSpatialRef() {
131-
return "cine" + System.currentTimeMillis() + "_" + (spatialId.incrementAndGet());
132-
}
133-
134-
/**
135-
* Recursively search the scene graph for a Spatial whose CINEMATIC_REF
136-
* matches the stored spatialRef.
137-
*
138-
* @param sp the root Spatial to start searching from (not null)
139-
* @return the matching Spatial, or null if not found
140-
*/
141-
private Spatial findModelByRef(Spatial sp) {
142-
String refId = sp.getUserData(CINEMATIC_REF);
143-
if (spatialRef.equals(refId)) {
144-
return sp;
145-
}
146-
147-
if (sp instanceof Node) {
148-
for (Spatial child : ((Node) sp).getChildren()) {
149-
Spatial model = findModelByRef(child);
150-
if (model != null) {
151-
return model;
152-
}
153-
}
154-
}
155-
156-
return null;
120+
animRef = "animEvent-" + System.currentTimeMillis() + "_" + refCounter.incrementAndGet();
157121
}
158122

159123
/**
@@ -165,24 +129,9 @@ private Spatial findModelByRef(Spatial sp) {
165129
@Override
166130
public void initEvent(Application app, Cinematic cinematic) {
167131
super.initEvent(app, cinematic);
168-
169-
if (composer == null) {
170-
Spatial model = findModelByRef(cinematic.getScene());
171-
if (model != null) {
172-
composer = model.getControl(AnimComposer.class);
173-
} else {
174-
throw new UnsupportedOperationException(
175-
"No Spatial found in the scene with Cinematic:Ref=" + spatialRef);
176-
}
177-
}
178-
}
179-
180-
@Override
181-
public void dispose() {
182-
super.dispose();
183-
composer = null;
184132
}
185133

134+
186135
/**
187136
* Callback when the event is paused.
188137
*/
@@ -320,10 +269,11 @@ public void setTime(float time) {
320269
@Override
321270
public void read(JmeImporter importer) throws IOException {
322271
super.read(importer);
323-
InputCapsule ic = importer.getCapsule(this);
324-
spatialRef = ic.readString("spatialRef", null);
325-
actionName = ic.readString("actionName", null);
326-
layerName = ic.readString("layerName", AnimComposer.DEFAULT_LAYER);
272+
InputCapsule capsule = importer.getCapsule(this);
273+
actionName = capsule.readString("actionName", "");
274+
composer = (AnimComposer) capsule.readSavable("composer", null);
275+
layerName = capsule.readString("layerName", AnimComposer.DEFAULT_LAYER);
276+
animRef = capsule.readString("animRef", null);
327277
}
328278

329279
/**
@@ -336,9 +286,37 @@ public void read(JmeImporter importer) throws IOException {
336286
@Override
337287
public void write(JmeExporter exporter) throws IOException {
338288
super.write(exporter);
339-
OutputCapsule oc = exporter.getCapsule(this);
340-
oc.write(spatialRef, "spatialRef", null);
341-
oc.write(actionName, "actionName", null);
342-
oc.write(layerName, "layerName", AnimComposer.DEFAULT_LAYER);
289+
OutputCapsule capsule = exporter.getCapsule(this);
290+
capsule.write(actionName, "actionName", "");
291+
capsule.write(composer, "composer", null);
292+
capsule.write(layerName, "layerName", AnimComposer.DEFAULT_LAYER);
293+
capsule.write(animRef, "animRef", null);
294+
}
295+
296+
public AnimComposer getComposer() {
297+
return composer;
298+
}
299+
300+
public void setComposer(AnimComposer composer) {
301+
this.composer = composer;
302+
}
303+
304+
public String getAnimRef() {
305+
return animRef;
343306
}
307+
308+
@Override
309+
public Object jmeClone() {
310+
try {
311+
return super.clone();
312+
} catch (CloneNotSupportedException e) {
313+
throw new RuntimeException("Can't clone AnimEvent", e);
314+
}
315+
}
316+
317+
@Override
318+
public void cloneFields(Cloner cloner, Object original) {
319+
320+
}
321+
344322
}

0 commit comments

Comments
 (0)