mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-12 01:58:45 +08:00
212 lines
6.6 KiB
Java
212 lines
6.6 KiB
Java
/******************************************************************************
|
|
* Spine Runtimes License Agreement
|
|
* Last updated April 5, 2025. Replaces all prior versions.
|
|
*
|
|
* Copyright (c) 2013-2025, Esoteric Software LLC
|
|
*
|
|
* Integration of the Spine Runtimes into software or otherwise creating
|
|
* derivative works of the Spine Runtimes is permitted under the terms and
|
|
* conditions of Section 2 of the Spine Editor License Agreement:
|
|
* http://esotericsoftware.com/spine-editor-license
|
|
*
|
|
* Otherwise, it is permitted to integrate the Spine Runtimes into software
|
|
* or otherwise create derivative works of the Spine Runtimes (collectively,
|
|
* "Products"), provided that each user of the Products must obtain their own
|
|
* Spine Editor license and redistribution of the Products in any form must
|
|
* include this license and copyright notice.
|
|
*
|
|
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
|
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
|
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
|
|
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*****************************************************************************/
|
|
|
|
package com.esotericsoftware.spine;
|
|
|
|
import com.badlogic.gdx.ApplicationListener;
|
|
import com.badlogic.gdx.Gdx;
|
|
import com.badlogic.gdx.backends.headless.HeadlessApplication;
|
|
import com.badlogic.gdx.backends.headless.HeadlessApplicationConfiguration;
|
|
import com.badlogic.gdx.files.FileHandle;
|
|
import com.badlogic.gdx.graphics.Texture;
|
|
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
|
|
import com.esotericsoftware.spine.utils.SkeletonSerializer;
|
|
|
|
public class HeadlessTest implements ApplicationListener {
|
|
private String skeletonPath;
|
|
private String atlasPath;
|
|
private String animationName;
|
|
|
|
public HeadlessTest (String skeletonPath, String atlasPath, String animationName) {
|
|
this.skeletonPath = skeletonPath;
|
|
this.atlasPath = atlasPath;
|
|
this.animationName = animationName;
|
|
}
|
|
|
|
static class MockTexture extends Texture {
|
|
private int width, height;
|
|
|
|
public MockTexture (int width, int height) {
|
|
super();
|
|
this.width = width;
|
|
this.height = height;
|
|
}
|
|
|
|
@Override
|
|
public int getWidth () {
|
|
return width;
|
|
}
|
|
|
|
@Override
|
|
public int getHeight () {
|
|
return height;
|
|
}
|
|
|
|
@Override
|
|
public void bind () {
|
|
}
|
|
|
|
@Override
|
|
public void bind (int unit) {
|
|
}
|
|
|
|
@Override
|
|
public void dispose () {
|
|
}
|
|
}
|
|
|
|
static class HeadlessTextureAtlas extends TextureAtlas {
|
|
public HeadlessTextureAtlas (FileHandle packFile) {
|
|
TextureAtlasData data = new TextureAtlasData(packFile, packFile.parent(), false);
|
|
|
|
for (TextureAtlasData.Page page : data.getPages()) {
|
|
page.texture = new MockTexture(1024, 1024);
|
|
}
|
|
|
|
for (TextureAtlasData.Region region : data.getRegions()) {
|
|
AtlasRegion atlasRegion = new AtlasRegion(region.page.texture, region.left, region.top,
|
|
region.rotate ? region.height : region.width, region.rotate ? region.width : region.height);
|
|
atlasRegion.index = region.index;
|
|
atlasRegion.name = region.name;
|
|
atlasRegion.offsetX = region.offsetX;
|
|
atlasRegion.offsetY = region.offsetY;
|
|
atlasRegion.originalHeight = region.originalHeight;
|
|
atlasRegion.originalWidth = region.originalWidth;
|
|
atlasRegion.rotate = region.rotate;
|
|
atlasRegion.degrees = region.degrees;
|
|
atlasRegion.names = region.names;
|
|
atlasRegion.values = region.values;
|
|
if (region.flip) atlasRegion.flip(false, true);
|
|
getRegions().add(atlasRegion);
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void create () {
|
|
try {
|
|
// Load atlas without textures
|
|
FileHandle atlasFile = Gdx.files.absolute(atlasPath);
|
|
TextureAtlas atlas = new HeadlessTextureAtlas(atlasFile);
|
|
|
|
// Load skeleton data
|
|
FileHandle skeletonFile = Gdx.files.absolute(skeletonPath);
|
|
SkeletonData skeletonData;
|
|
|
|
if (skeletonPath.endsWith(".json")) {
|
|
SkeletonJson json = new SkeletonJson(atlas);
|
|
skeletonData = json.readSkeletonData(skeletonFile);
|
|
} else {
|
|
SkeletonBinary binary = new SkeletonBinary(atlas);
|
|
skeletonData = binary.readSkeletonData(skeletonFile);
|
|
}
|
|
|
|
// Create serializer
|
|
SkeletonSerializer serializer = new SkeletonSerializer();
|
|
|
|
// Print skeleton data as JSON
|
|
System.out.println("=== SKELETON DATA ===");
|
|
System.out.println(serializer.serializeSkeletonData(skeletonData));
|
|
|
|
// Create skeleton instance
|
|
Skeleton skeleton = new Skeleton(skeletonData);
|
|
|
|
// Set animation if provided
|
|
AnimationState state = null;
|
|
if (animationName != null) {
|
|
// Create animation state only when needed
|
|
AnimationStateData stateData = new AnimationStateData(skeletonData);
|
|
state = new AnimationState(stateData);
|
|
|
|
// Find and set animation
|
|
Animation animation = skeletonData.findAnimation(animationName);
|
|
if (animation == null) {
|
|
System.err.println("Animation not found: " + animationName);
|
|
System.exit(1);
|
|
}
|
|
state.setAnimation(0, animation, true);
|
|
// Update and apply
|
|
state.update(0.016f);
|
|
state.apply(skeleton);
|
|
}
|
|
|
|
skeleton.updateWorldTransform(Physics.update);
|
|
|
|
// Print skeleton state as JSON
|
|
System.out.println("\n=== SKELETON STATE ===");
|
|
System.out.println(serializer.serializeSkeleton(skeleton));
|
|
|
|
// Print animation state as JSON only if animation was loaded
|
|
if (state != null) {
|
|
System.out.println("\n=== ANIMATION STATE ===");
|
|
System.out.println(serializer.serializeAnimationState(state));
|
|
}
|
|
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
System.exit(1);
|
|
}
|
|
|
|
// Exit after processing
|
|
Gdx.app.exit();
|
|
}
|
|
|
|
@Override
|
|
public void resize (int width, int height) {
|
|
}
|
|
|
|
@Override
|
|
public void render () {
|
|
}
|
|
|
|
@Override
|
|
public void pause () {
|
|
}
|
|
|
|
@Override
|
|
public void resume () {
|
|
}
|
|
|
|
@Override
|
|
public void dispose () {
|
|
}
|
|
|
|
public static void main (String[] args) {
|
|
if (args.length < 2) {
|
|
System.err.println("Usage: HeadlessTest <skeleton-path> <atlas-path> [animation-name]");
|
|
System.exit(1);
|
|
}
|
|
|
|
HeadlessApplicationConfiguration config = new HeadlessApplicationConfiguration();
|
|
config.updatesPerSecond = 60;
|
|
String animationName = args.length >= 3 ? args[2] : null;
|
|
new HeadlessApplication(new HeadlessTest(args[0], args[1], animationName), config);
|
|
}
|
|
}
|