-
Notifications
You must be signed in to change notification settings - Fork 63
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
What's the easiest way to read camera animation from a glTF file? #82
Comments
The API exposes the data that is contained in the glTF asset. Processing or interpreting that data is largely left to the client. That refers to rendering the data, as well as highly specific tasks like the one that you described. You are right: The concepts of But there is another layer of complexity when reading this data - namely, when there are animations involved. A "naive" requirement could be: "I want to read the data, as it is, at a certain state of the animation". But this immediately raises a whole lot of questions: What should be the 'state' (i.e. the animation time)? Which animations should be 'running'? Which part of the data is the one that you actually want to access? (Pure node transforms, or also things like morphed or skinned vertex coordinates?) As such, this question has some similarities to #80 . Two differences are
I am (roughly) aware of the possible requirements here. As mentioned above, they could be described as the requirement to "access an intermediate 'state' of an animated scene". But due to the subtle, technical details, there is not (yet) a full-fledged, convenient, public API for that. A basic approach could look as follows - but NOTE that this uses some PRELIMINARY API (and even calls a method that is currently still package de.javagl.jgltf.issues;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import de.javagl.jgltf.model.AnimationModel;
import de.javagl.jgltf.model.CameraModel;
import de.javagl.jgltf.model.GltfAnimations;
import de.javagl.jgltf.model.GltfModel;
import de.javagl.jgltf.model.NodeModel;
import de.javagl.jgltf.model.animation.Animation;
import de.javagl.jgltf.model.animation.AnimationManager;
import de.javagl.jgltf.model.animation.AnimationManager.AnimationPolicy;
import de.javagl.jgltf.model.io.GltfModelReader;
public class JgltfIssue82_ReadCameraAnimationData
{
public static void main(String[] args) throws IOException
{
String path = "AnimatedCameras.gltf";
GltfModelReader r = new GltfModelReader();
GltfModel gltfModel = r.read(Paths.get(path));
AnimationManager animationManager = createAnimationManager(gltfModel);
List<CameraInstance> cameraInstances =
computeCameraInstances(gltfModel);
printAnimatedCameraTransforms(animationManager, cameraInstances);
}
// An "instance" of a camera - namely, a camera and the node
// that the camera is attached to
static class CameraInstance
{
NodeModel nodeModel;
CameraModel cameraModel;
}
// Compute all camera instances in the given glTF model
private static List<CameraInstance>
computeCameraInstances(GltfModel gltfModel)
{
List<CameraInstance> animatedCameras = new ArrayList<CameraInstance>();
List<NodeModel> nodeModels = gltfModel.getNodeModels();
for (int i = 0; i < nodeModels.size(); i++)
{
NodeModel nodeModel = nodeModels.get(i);
CameraModel cameraModel = nodeModel.getCameraModel();
if (cameraModel != null)
{
CameraInstance animatedCamera = new CameraInstance();
animatedCamera.nodeModel = nodeModel;
animatedCamera.cameraModel = cameraModel;
animatedCameras.add(animatedCamera);
}
}
return animatedCameras;
}
// Create an AnimationManager for ALL animations in the given model.
// NOTE: This part of the API is HIGHLY preliminary, and may change
// arbitrarily in future releases!!!
private static AnimationManager createAnimationManager(GltfModel gltfModel)
{
List<AnimationModel> animationModels = gltfModel.getAnimationModels();
List<Animation> modelAnimations =
GltfAnimations.createModelAnimations(animationModels);
AnimationManager animationManager =
GltfAnimations.createAnimationManager(AnimationPolicy.ONCE);
animationManager.addAnimations(modelAnimations);
return animationManager;
}
private static void printAnimatedCameraTransforms(
AnimationManager animationManager, List<CameraInstance> cameraInstances)
{
long totalMs = 0;
// Print the state of the given camera instances at certain
// times of the animation
for (int s = 0; s < 10; s++)
{
// Perform an animation step
long msStep = 10;
long nsStep = msStep * 1000 * 1000;
callPerformStep(animationManager, nsStep);
totalMs += msStep;
// Print the state of the camera instance at the resulting time:
for (int i = 0; i < cameraInstances.size(); i++)
{
CameraInstance cameraInstance = cameraInstances.get(i);
NodeModel nodeModel = cameraInstance.nodeModel;
float[] matrix = new float[16];
nodeModel.computeGlobalTransform(matrix);
System.out.println("Global transform of camera " + i + " after "
+ totalMs + "ms is " + Arrays.toString(matrix));
}
}
}
// HACK! Call AnimationManager#performStep, which currently is 'private'
private static void callPerformStep(
AnimationManager animationManager, long ns)
{
try
{
Method method = AnimationManager.class
.getDeclaredMethod("performStep", long.class);
method.setAccessible(true);
method.invoke(animationManager, ns);
}
catch (NoSuchMethodException | SecurityException
| IllegalAccessException | IllegalArgumentException
| InvocationTargetException e)
{
e.printStackTrace();
}
}
} The following is a glTF asset that contains two cameras that are attached to nodes that are affected by an animation:
Running the program on this asset will print something like this:
I.e. it will print the global transform of the nodes that the cameras are attached to. This global transform describes the position and rotation (i.e. the orientation) of the camera instance. There are many assumptions hidden in that approach. But it might be sufficient for extracting the "animation data of a camera", as a first shot. I can try to allocate some time for improving the 'animation model' API part, to more easily and generically allow access to 'intermediate (animated) states of the scene'. But I can not make any promises about the timeline here. JglTF is a one-man, spare-time project, FWIW... |
Thanks! This will probably work (I'm coding in a volatile environment, so I'm not too concerned with stability for now). In a previous issue, I had mentioned the animation manager and was trying to figure out how to retrieve an animation without relying on system time. It's good to know that I'm not an idiot and this is an actual limitation with the software. Is it possible to get the underlying frame rate of the animation btw? Because the system I'm importing into does its own keyframe-based interpolation, it would be better maintain precision by importing keyframes at the rate used in the original file. |
Yes, some questions or aspects here are also related to the issue around #71 (comment) . I mentioned that there is no "simple, official" way to select a certain animation time. In fact, some aspects of the animation playback in glTF are far trickier than they look at the first glance....
That's one of these aspects 😁 : There is no "frame rate". glTF does not really dictate many aspects of the runtime behavior. It is, roughly speaking, a "representation of a truth" - plain data, and clients can do with this whatever they want. Specifically: The animation maps an "input time" to "output values". The input time is given in seconds. When the animation has a duration of 1 second, and you have a fast PC that renders with 60FPS, then you'll see 60 intermediate steps. When you have a slow PC that only renders with 10FPS, then you'll only see 10 intermediate steps. In the drafted snippet, this can be seen at
It just shows the state at simulation times of 10ms, 20ms, 30ms... You could reduce the step size to This, in turn, raises many questions about the target application that you are reading the data for.
It will almost certainly support something like "key frames" as well (and probably different interpolation types, like (Again: There are many assuptions and guesses involved here...) |
Interesting. I didn't know this! Yeah, it's still Minecraft, but a slightly different use-case. I'm trying to mod the the Replay Mod be able to import camera animations from external programs rather than restricting the user to its in-built editor. |
Also, I'm really impressed with your work in this repo. When I first found it, I thought it was maintained by a whole group of people. |
I'm using JglTF to write an importer for camera animation in a piece of software, and I need to read the animation data of a camera from a glTF file. However, all the buffers and buffer views that are part of the API are confusing me. What would be the simplest approach for retrieving this data?
The text was updated successfully, but these errors were encountered: