Hi
Can someone please explain why this code needs to be run in every update loop?
I mean why the target list is cleared and refilled every frame? How can it be different?
Regards
Hi
Can someone please explain why this code needs to be run in every update loop?
I mean why the target list is cleared and refilled every frame? How can it be different?
Regards
I havnât used the MorphControl yet but i assume it can be added to a Node which might have children attached and detached at runtime.
It is strange though that a SafeArrayList is used for the targets, not using the toArray() method when iterating but instead creating an iterator each frame (internally creating an array) so it makes for quite some garbage each frame i guess, so just using an ArrayList might be an easier approach with the downside that it wouldnt shrink when the number of targets is reduced
I wonder if that code is meant to initialize like the first time or something⌠since controls donât really have an initialize().
Itâs weird. The git blame was not particularly illuminating either⌠but does add some context.
2 more thoughts reading the MorphControl class again:
the way the maximum number of targets is determined is performance heavy but precise, however the result is cached (probably because its performance heavy). But this cached value is only reset when a new Material is set, although in theory this could change when changing a material parameter that is bound to a define, while such vertex attrib wouldnt neccessarily need to be directly surrounded by an #ifdef something but instead could be optimized away by the compiler when it finds the attrib is not used dues to an #ifdef somewhere else. Not sure any built-in shader is a potential candidate for it but might be worth at least stating in the javadoc
secondly, i am wondering about the comment stating whats done in the update method needs to be done in the update method. I dont see why it cannot be done in the render method?
(so it would be skipped when its culled, but potentially be run several times if its part of and visible in several viewports scenes)
Guys, thanks for the response
I think so. Thatâs unfortunate.
I modified it like this in my test and works without issue.
public void resetTargets() {
// gathering geometries in the sub graph.
// This must be done in the update phase as the gathering might add a matparam override
targets.clear();
spatial.depthFirstTraversal(targetLocator);
}
@Override
protected void controlUpdate(float tpf) {
if (!enabled) {
return;
}
if (targets.isEmpty()) {
resetTargets();
}
}
Hi guys, a little tip:
the MorphControl class extends AbstractControl. The controlUpdate
and controlRenderer
methods are not invoked if the controller is disabled. So checking if the controller is enabled in such methods is useless right? You could remove these instructions:
@Override
protected void controlUpdate(float tpf) {
if (!enabled) { // this check is useless
return;
}
....
}
@Override
protected void controlRender(RenderManager rm, ViewPort vp) {
if (!enabled) { // this check is useless
return;
}
...
}
From AbstractControl
class:
@Override
public void update(float tpf) {
if (!enabled)
return;
controlUpdate(tpf);
}
@Override
public void render(RenderManager rm, ViewPort vp) {
if (!enabled)
return;
controlRender(rm, vp);
}
Right, thanks.
If I call it from the render method, it throws this exception
SEVERE: Uncaught exception thrown in Thread[jME3 Main,5,main]
java.lang.IllegalStateException: Scene graph is not properly updated for rendering.
State was changed after rootNode.updateGeometricState() call.
Make sure you do not modify the scene from another thread!
Problem spatial name: cloth_0000061:Mesh
at com.jme3.scene.Spatial.checkCulling(Spatial.java:365)
at com.jme3.renderer.RenderManager.renderSubScene(RenderManager.java:717)
at com.jme3.renderer.RenderManager.renderSubScene(RenderManager.java:731)
at com.jme3.renderer.RenderManager.renderSubScene(RenderManager.java:731)
at com.jme3.renderer.RenderManager.renderSubScene(RenderManager.java:731)
at com.jme3.renderer.RenderManager.renderSubScene(RenderManager.java:731)
at com.jme3.renderer.RenderManager.renderSubScene(RenderManager.java:731)
at com.jme3.renderer.RenderManager.renderSubScene(RenderManager.java:731)
at com.jme3.renderer.RenderManager.renderSubScene(RenderManager.java:731)
at com.jme3.renderer.RenderManager.renderSubScene(RenderManager.java:731)
at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:710)
at com.jme3.renderer.RenderManager.renderViewPort(RenderManager.java:1096)
at com.jme3.renderer.RenderManager.render(RenderManager.java:1158)
at com.jme3.app.SimpleApplication.update(SimpleApplication.java:271)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.runLoop(LwjglAbstractDisplay.java:160)
at com.jme3.system.lwjgl.LwjglDisplay.runLoop(LwjglDisplay.java:196)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:242)
at java.lang.Thread.run(Thread.java:748)
which seems to be caused by mat param overriding as If I comment this line it wont throw the exception.
Oh ye that makes sence, didnt realize matParamOverrides are tracked in the refresh flags the same way other state is but it makes sence as they can be inherited though the scenegraph
thanks for playing around with it
I was testing morph animation (the Synchronicity) on different types of models and here is the result.
1- Model has only morph animation. (do not have bone (skin) animation)
I can play animations asynchronously on multiple clones without issue at the same time.
2- Model has morph and bone (skin) animation on the same geometry.
like this model
I can play animations asynchronously on multiple clones without issue at the same time. Note, in this case, meshes will be a semi-deep clone because they are skinned. (cloned with mesh.cloneForAnim()
)
3- Model has morph and bone (skin) animation but on different geometries.
Like this model
The body has bone animation and the skirt has morph animation.
I can not play animation asynchronously on multiple clones unless I deep clone the morphed meshes. (skirt in this case). Note in this case body will use a semi-deep clone and the skirt will use a shallow clone.
I wonder why morph works fine with shallow clone in case 1 but does not in case 3?
Just to get sure I am understanding this line correctly!
So does the cloner clone the targets
list from the original spatial and automatically replace the items in it with their clones?
Edit:
Ok, never mind, seems it does
so as i see you fixed it in:
? i dont understand what was the issue but i see things like removeMatParamOverride there.
And the PR settles it so it will just run once and not every frame.
sounds serious, thanks for PR
In the PR I submitted, this wonât be done automatically so the user needs to detach and reattach the MorphControl for the list to get updated. This is a rare usecase, though.
That seems worth mentioning in the javadoc.
Okay, I was wrong.
In my test, the model had only two morph targets. After testing with another model with more morph targets I noticed the glitches on the mesh when animation is played asynchronously.
I do not know why this happens. Are morphs handled on GPU or CPU? If they all modify the same mesh buffers?
Anyway!
In the first video, I use a simple morphed model with 2 morph targets. there seems to be no issue when playing asynchronously.
The 2nd one has 3 morph targets and you can notice some glitches on the mesh while one of them is playing and the other one is stopped.
3rd one has around 30 morph targets on the skirt mesh. As you see if I stop one of them the other one is fully influencing it and if they played asynchronously you can notice the glitches on the mesh.
I was able to fix the issue by doing
model.depthFirstTraversal(new SceneGraphVisitorAdapter() {
@Override
public void visit(Geometry geom) {
if(geom.getMesh().hasMorphTargets()) {
geom.setMesh(geom.getMesh().cloneForAnim());
}
}
});
So to sum it up. I believe we need to update the Geometry.cloneField() to use cloneForAnim if the mesh is morphed. Similar to what we are doing for skinned meshes here:
I believe morphing can be performed by either the CPU or the GPU. The actual mix depends on the number of morph targets and the number of available slots, which is determined dynamically.
I think youâre right.