mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2025-12-21 01:36:02 +08:00
[libgdx] Add DebugPrinter program, add runDebugPrinter task to build.gradle
This commit is contained in:
parent
497f4382b1
commit
9ee4d6c40e
@ -138,11 +138,22 @@ configure(subprojects - project("spine-libgdx")) {
|
||||
implementation "com.badlogicgames.gdx:gdx:$libgdxVersion"
|
||||
implementation "com.badlogicgames.gdx:gdx-platform:$libgdxVersion:natives-desktop"
|
||||
implementation "com.badlogicgames.gdx:gdx-backend-lwjgl3:$libgdxVersion"
|
||||
implementation "com.badlogicgames.gdx:gdx-backend-headless:$libgdxVersion"
|
||||
implementation "com.badlogicgames.gdx:gdx-box2d:$libgdxVersion"
|
||||
implementation "com.badlogicgames.gdx:gdx-box2d-platform:$libgdxVersion:natives-desktop"
|
||||
}
|
||||
}
|
||||
|
||||
project("spine-libgdx-tests") {
|
||||
task runDebugPrinter(type: JavaExec) {
|
||||
main = 'com.esotericsoftware.spine.DebugPrinter'
|
||||
classpath = sourceSets.main.runtimeClasspath
|
||||
if (project.hasProperty('args')) {
|
||||
args project.getProperty('args').split(' ')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile).configureEach {
|
||||
println "Building with sourceCompatibility = ${sourceCompatibility}, targetCompatibility = ${targetCompatibility}"
|
||||
}
|
||||
|
||||
@ -0,0 +1,283 @@
|
||||
/******************************************************************************
|
||||
* 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.badlogic.gdx.graphics.g2d.TextureAtlas.AtlasRegion;
|
||||
import com.badlogic.gdx.graphics.g2d.TextureAtlas.TextureAtlasData;
|
||||
|
||||
public class DebugPrinter implements ApplicationListener {
|
||||
private String skeletonPath;
|
||||
private String atlasPath;
|
||||
private String animationName;
|
||||
|
||||
public DebugPrinter (String skeletonPath, String atlasPath, String animationName) {
|
||||
this.skeletonPath = skeletonPath;
|
||||
this.atlasPath = atlasPath;
|
||||
this.animationName = animationName;
|
||||
}
|
||||
|
||||
static class Printer {
|
||||
private int indentLevel = 0;
|
||||
private static final String INDENT = " ";
|
||||
|
||||
private void print (String text) {
|
||||
for (int i = 0; i < indentLevel; i++) {
|
||||
System.out.print(INDENT);
|
||||
}
|
||||
System.out.println(text);
|
||||
}
|
||||
|
||||
private void printValue (String name, Object value) {
|
||||
if (value == null) {
|
||||
print(name + ": null");
|
||||
} else if (value instanceof String) {
|
||||
print(name + ": \"" + value + "\"");
|
||||
} else if (value instanceof Float) {
|
||||
// Format floats to 6 decimal places to match other runtimes
|
||||
print(name + ": " + String.format("%.6f", value));
|
||||
} else {
|
||||
print(name + ": " + value);
|
||||
}
|
||||
}
|
||||
|
||||
private void indent () {
|
||||
indentLevel++;
|
||||
}
|
||||
|
||||
private void unindent () {
|
||||
indentLevel--;
|
||||
}
|
||||
|
||||
public void printSkeletonData (SkeletonData data) {
|
||||
print("SkeletonData {");
|
||||
indent();
|
||||
|
||||
// Basic properties
|
||||
printValue("name", data.getName());
|
||||
printValue("version", data.getVersion());
|
||||
printValue("hash", data.getHash());
|
||||
printValue("x", data.getX());
|
||||
printValue("y", data.getY());
|
||||
printValue("width", data.getWidth());
|
||||
printValue("height", data.getHeight());
|
||||
printValue("referenceScale", data.getReferenceScale());
|
||||
printValue("fps", data.getFps());
|
||||
printValue("imagesPath", data.getImagesPath());
|
||||
printValue("audioPath", data.getAudioPath());
|
||||
|
||||
// TODO: Add bones, slots, skins, animations, etc. in future expansion
|
||||
|
||||
unindent();
|
||||
print("}");
|
||||
}
|
||||
|
||||
public void printSkeleton (Skeleton skeleton) {
|
||||
print("Skeleton {");
|
||||
indent();
|
||||
|
||||
// Basic properties
|
||||
printValue("x", skeleton.getX());
|
||||
printValue("y", skeleton.getY());
|
||||
printValue("scaleX", skeleton.getScaleX());
|
||||
printValue("scaleY", skeleton.getScaleY());
|
||||
printValue("time", skeleton.getTime());
|
||||
|
||||
// TODO: Add runtime state (bones, slots, etc.) in future expansion
|
||||
|
||||
unindent();
|
||||
print("}");
|
||||
}
|
||||
}
|
||||
|
||||
// Mock texture that doesn't require OpenGL - similar to AndroidTexture
|
||||
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 () {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind (int unit) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose () {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
|
||||
// Custom TextureAtlas that doesn't load actual textures - similar to AndroidTextureAtlas
|
||||
static class HeadlessTextureAtlas extends TextureAtlas {
|
||||
public HeadlessTextureAtlas (FileHandle packFile) {
|
||||
// Load atlas data without creating real textures
|
||||
TextureAtlasData data = new TextureAtlasData(packFile, packFile.parent(), false);
|
||||
|
||||
// Create mock textures for each page
|
||||
for (TextureAtlasData.Page page : data.getPages()) {
|
||||
// Create a mock texture - we use 1024x1024 as a default size
|
||||
page.texture = new MockTexture(1024, 1024);
|
||||
}
|
||||
|
||||
// Create regions from the data
|
||||
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);
|
||||
}
|
||||
|
||||
// Print skeleton data
|
||||
System.out.println("=== SKELETON DATA ===");
|
||||
Printer printer = new Printer();
|
||||
printer.printSkeletonData(skeletonData);
|
||||
|
||||
// Create skeleton instance
|
||||
Skeleton skeleton = new Skeleton(skeletonData);
|
||||
|
||||
// Create animation state
|
||||
AnimationStateData stateData = new AnimationStateData(skeletonData);
|
||||
AnimationState 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.update(0.016f);
|
||||
skeleton.updateWorldTransform(Physics.update);
|
||||
|
||||
// Print skeleton state
|
||||
System.out.println("\n=== SKELETON STATE ===");
|
||||
printer.printSkeleton(skeleton);
|
||||
|
||||
} 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 < 3) {
|
||||
System.err.println("Usage: DebugPrinter <skeleton-path> <atlas-path> <animation-name>");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
HeadlessApplicationConfiguration config = new HeadlessApplicationConfiguration();
|
||||
config.updatesPerSecond = 60;
|
||||
new HeadlessApplication(new DebugPrinter(args[0], args[1], args[2]), config);
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user