mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-04 14:24:53 +08:00
Add Debug Renderer with and before/after paint callbacks
This commit is contained in:
parent
8df1b3ee27
commit
5ed26e6ef5
@ -0,0 +1,64 @@
|
|||||||
|
package com.esotericsoftware.spine
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.rounded.ArrowBack
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TopAppBar
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.viewinterop.AndroidView
|
||||||
|
import androidx.navigation.NavHostController
|
||||||
|
import com.esotericsoftware.spine.android.DebugRenderer
|
||||||
|
import com.esotericsoftware.spine.android.SpineController
|
||||||
|
import com.esotericsoftware.spine.android.SpineView
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun DebugRendering(nav: NavHostController) {
|
||||||
|
|
||||||
|
val debugRenderer = remember {
|
||||||
|
DebugRenderer()
|
||||||
|
}
|
||||||
|
|
||||||
|
Scaffold(
|
||||||
|
topBar = {
|
||||||
|
TopAppBar(
|
||||||
|
title = { Text(text = Destination.DebugRendering.title) },
|
||||||
|
navigationIcon = {
|
||||||
|
IconButton({ nav.navigateUp() }) {
|
||||||
|
Icon(
|
||||||
|
Icons.Rounded.ArrowBack,
|
||||||
|
null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) { paddingValues ->
|
||||||
|
AndroidView(
|
||||||
|
factory = { ctx ->
|
||||||
|
SpineView(ctx).apply {
|
||||||
|
loadFromAsset(
|
||||||
|
"spineboy.atlas",
|
||||||
|
"spineboy-pro.json",
|
||||||
|
SpineController.Builder()
|
||||||
|
.setOnInitialized {
|
||||||
|
it.animationState.setAnimation(0, "walk", true)
|
||||||
|
}
|
||||||
|
.setOnAfterPaint { controller, canvas, commands ->
|
||||||
|
debugRenderer.render(controller.drawable, canvas, commands)
|
||||||
|
}
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modifier = Modifier.padding(paddingValues)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -61,6 +61,7 @@ fun AppContent() {
|
|||||||
Destination.SimpleAnimation,
|
Destination.SimpleAnimation,
|
||||||
Destination.PlayPause,
|
Destination.PlayPause,
|
||||||
Destination.AnimationStateEvents,
|
Destination.AnimationStateEvents,
|
||||||
|
Destination.DebugRendering,
|
||||||
Destination.IKFollowing,
|
Destination.IKFollowing,
|
||||||
Destination.Physics
|
Destination.Physics
|
||||||
),
|
),
|
||||||
@ -87,6 +88,12 @@ fun AppContent() {
|
|||||||
AnimationState(navController)
|
AnimationState(navController)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
composable(
|
||||||
|
Destination.DebugRendering.route
|
||||||
|
) {
|
||||||
|
DebugRendering(navController)
|
||||||
|
}
|
||||||
|
|
||||||
composable(
|
composable(
|
||||||
Destination.IKFollowing.route
|
Destination.IKFollowing.route
|
||||||
) {
|
) {
|
||||||
@ -134,6 +141,7 @@ sealed class Destination(val route: String, val title: String) {
|
|||||||
data object Samples: Destination("samples", "Spine Android Examples")
|
data object Samples: Destination("samples", "Spine Android Examples")
|
||||||
data object SimpleAnimation : Destination("simpleAnimation", "Simple Animation")
|
data object SimpleAnimation : Destination("simpleAnimation", "Simple Animation")
|
||||||
data object PlayPause : Destination("playPause", "Play/Pause")
|
data object PlayPause : Destination("playPause", "Play/Pause")
|
||||||
|
data object DebugRendering: Destination("debugRendering", "Debug Renderer")
|
||||||
data object AnimationStateEvents : Destination("animationStateEvents", "Animation State Listener")
|
data object AnimationStateEvents : Destination("animationStateEvents", "Animation State Listener")
|
||||||
data object IKFollowing : Destination("ikFollowing", "IK Following")
|
data object IKFollowing : Destination("ikFollowing", "IK Following")
|
||||||
data object Physics: Destination("physics", "Physics (drag anywhere)")
|
data object Physics: Destination("physics", "Physics (drag anywhere)")
|
||||||
|
|||||||
@ -0,0 +1,23 @@
|
|||||||
|
package com.esotericsoftware.spine.android;
|
||||||
|
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.RectF;
|
||||||
|
|
||||||
|
import com.badlogic.gdx.utils.Array;
|
||||||
|
import com.esotericsoftware.spine.Bone;
|
||||||
|
|
||||||
|
public class DebugRenderer {
|
||||||
|
|
||||||
|
public void render(AndroidSkeletonDrawable drawable, Canvas canvas, Array<SkeletonRenderer.RenderCommand> commands) {
|
||||||
|
Paint bonePaint = new Paint();
|
||||||
|
bonePaint.setColor(android.graphics.Color.BLUE);
|
||||||
|
bonePaint.setStyle(Paint.Style.FILL);
|
||||||
|
|
||||||
|
for (Bone bone : drawable.getSkeleton().getBones()) {
|
||||||
|
float x = bone.getWorldX();
|
||||||
|
float y = bone.getWorldY();
|
||||||
|
canvas.drawRect(new RectF(x - 2.5f, y - 2.5f, x + 2.5f, y + 2.5f), bonePaint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -205,8 +205,7 @@ public class SkeletonRenderer {
|
|||||||
return commandList;
|
return commandList;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void render (Canvas canvas, Skeleton skeleton) {
|
public void render (Canvas canvas, Array<RenderCommand> commands) {
|
||||||
Array<RenderCommand> commands = render(skeleton);
|
|
||||||
for (int i = 0; i < commands.size; i++) {
|
for (int i = 0; i < commands.size; i++) {
|
||||||
RenderCommand command = commands.get(i);
|
RenderCommand command = commands.get(i);
|
||||||
canvas.drawVertices(Canvas.VertexMode.TRIANGLES, command.vertices.size, command.vertices.items, 0, command.uvs.items, 0,
|
canvas.drawVertices(Canvas.VertexMode.TRIANGLES, command.vertices.size, command.vertices.items, 0, command.uvs.items, 0,
|
||||||
|
|||||||
@ -1,13 +1,17 @@
|
|||||||
package com.esotericsoftware.spine.android;
|
package com.esotericsoftware.spine.android;
|
||||||
|
|
||||||
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Point;
|
import android.graphics.Point;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.badlogic.gdx.utils.Array;
|
||||||
import com.esotericsoftware.spine.AnimationState;
|
import com.esotericsoftware.spine.AnimationState;
|
||||||
import com.esotericsoftware.spine.AnimationStateData;
|
import com.esotericsoftware.spine.AnimationStateData;
|
||||||
import com.esotericsoftware.spine.Skeleton;
|
import com.esotericsoftware.spine.Skeleton;
|
||||||
import com.esotericsoftware.spine.SkeletonData;
|
import com.esotericsoftware.spine.SkeletonData;
|
||||||
|
import com.esotericsoftware.spine.android.utils.SpineControllerAfterPaintCallback;
|
||||||
|
import com.esotericsoftware.spine.android.utils.SpineControllerBeforePaintCallback;
|
||||||
import com.esotericsoftware.spine.android.utils.SpineControllerCallback;
|
import com.esotericsoftware.spine.android.utils.SpineControllerCallback;
|
||||||
|
|
||||||
public class SpineController {
|
public class SpineController {
|
||||||
@ -16,6 +20,8 @@ public class SpineController {
|
|||||||
private SpineControllerCallback onInitialized;
|
private SpineControllerCallback onInitialized;
|
||||||
private SpineControllerCallback onBeforeUpdateWorldTransforms;
|
private SpineControllerCallback onBeforeUpdateWorldTransforms;
|
||||||
private SpineControllerCallback onAfterUpdateWorldTransforms;
|
private SpineControllerCallback onAfterUpdateWorldTransforms;
|
||||||
|
private SpineControllerBeforePaintCallback onBeforePaint;
|
||||||
|
private SpineControllerAfterPaintCallback onAfterPaint;
|
||||||
|
|
||||||
public Builder setOnInitialized(SpineControllerCallback onInitialized) {
|
public Builder setOnInitialized(SpineControllerCallback onInitialized) {
|
||||||
this.onInitialized = onInitialized;
|
this.onInitialized = onInitialized;
|
||||||
@ -32,11 +38,23 @@ public class SpineController {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder setOnBeforePaint(SpineControllerBeforePaintCallback onBeforePaint) {
|
||||||
|
this.onBeforePaint = onBeforePaint;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setOnAfterPaint(SpineControllerAfterPaintCallback onAfterPaint) {
|
||||||
|
this.onAfterPaint = onAfterPaint;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public SpineController build() {
|
public SpineController build() {
|
||||||
SpineController spineController = new SpineController();
|
SpineController spineController = new SpineController();
|
||||||
spineController.onInitialized = onInitialized;
|
spineController.onInitialized = onInitialized;
|
||||||
spineController.onBeforeUpdateWorldTransforms = onBeforeUpdateWorldTransforms;
|
spineController.onBeforeUpdateWorldTransforms = onBeforeUpdateWorldTransforms;
|
||||||
spineController.onAfterUpdateWorldTransforms = onAfterUpdateWorldTransforms;
|
spineController.onAfterUpdateWorldTransforms = onAfterUpdateWorldTransforms;
|
||||||
|
spineController.onBeforePaint = onBeforePaint;
|
||||||
|
spineController.onAfterPaint = onAfterPaint;
|
||||||
return spineController;
|
return spineController;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -44,6 +62,8 @@ public class SpineController {
|
|||||||
private @Nullable SpineControllerCallback onInitialized;
|
private @Nullable SpineControllerCallback onInitialized;
|
||||||
private @Nullable SpineControllerCallback onBeforeUpdateWorldTransforms;
|
private @Nullable SpineControllerCallback onBeforeUpdateWorldTransforms;
|
||||||
private @Nullable SpineControllerCallback onAfterUpdateWorldTransforms;
|
private @Nullable SpineControllerCallback onAfterUpdateWorldTransforms;
|
||||||
|
private @Nullable SpineControllerBeforePaintCallback onBeforePaint;
|
||||||
|
private @Nullable SpineControllerAfterPaintCallback onAfterPaint;
|
||||||
private AndroidSkeletonDrawable drawable;
|
private AndroidSkeletonDrawable drawable;
|
||||||
private boolean playing = true;
|
private boolean playing = true;
|
||||||
private double offsetX = 0;
|
private double offsetX = 0;
|
||||||
@ -83,7 +103,7 @@ public class SpineController {
|
|||||||
return drawable.getAnimationState();
|
return drawable.getAnimationState();
|
||||||
}
|
}
|
||||||
|
|
||||||
AndroidSkeletonDrawable getDrawable() {
|
public AndroidSkeletonDrawable getDrawable() {
|
||||||
if (drawable == null) throw new RuntimeException("Controller is not initialized yet.");
|
if (drawable == null) throw new RuntimeException("Controller is not initialized yet.");
|
||||||
return drawable;
|
return drawable;
|
||||||
}
|
}
|
||||||
@ -128,4 +148,16 @@ public class SpineController {
|
|||||||
onAfterUpdateWorldTransforms.execute(this);
|
onAfterUpdateWorldTransforms.execute(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void callOnBeforePaint(Canvas canvas) {
|
||||||
|
if (onBeforePaint != null) {
|
||||||
|
onBeforePaint.execute(this, canvas);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void callOnAfterPaint(Canvas canvas, Array<SkeletonRenderer.RenderCommand> renderCommands) {
|
||||||
|
if (onAfterPaint != null) {
|
||||||
|
onAfterPaint.execute(this, canvas, renderCommands);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -29,7 +29,7 @@
|
|||||||
|
|
||||||
package com.esotericsoftware.spine.android;
|
package com.esotericsoftware.spine.android;
|
||||||
|
|
||||||
import com.badlogic.gdx.math.Vector2;
|
import com.badlogic.gdx.utils.Array;
|
||||||
import com.esotericsoftware.spine.android.bounds.Alignment;
|
import com.esotericsoftware.spine.android.bounds.Alignment;
|
||||||
import com.esotericsoftware.spine.android.bounds.Bounds;
|
import com.esotericsoftware.spine.android.bounds.Bounds;
|
||||||
import com.esotericsoftware.spine.android.bounds.BoundsProvider;
|
import com.esotericsoftware.spine.android.bounds.BoundsProvider;
|
||||||
@ -153,11 +153,15 @@ public class SpineView extends View implements Choreographer.FrameCallback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
canvas.save();
|
canvas.save();
|
||||||
|
|
||||||
canvas.translate(offsetX, offsetY);
|
canvas.translate(offsetX, offsetY);
|
||||||
canvas.scale(scaleX, scaleY * -1);
|
canvas.scale(scaleX, scaleY * -1);
|
||||||
canvas.translate(x, y);
|
canvas.translate(x, y);
|
||||||
|
|
||||||
renderer.render(canvas, controller.getSkeleton());
|
controller.callOnBeforePaint(canvas);
|
||||||
|
Array<SkeletonRenderer.RenderCommand> commands = renderer.render(controller.getSkeleton());
|
||||||
|
renderer.render(canvas, commands);
|
||||||
|
controller.callOnAfterPaint(canvas, commands);
|
||||||
|
|
||||||
canvas.restore();
|
canvas.restore();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,14 @@
|
|||||||
|
package com.esotericsoftware.spine.android.utils;
|
||||||
|
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
|
||||||
|
import com.badlogic.gdx.utils.Array;
|
||||||
|
import com.esotericsoftware.spine.android.SkeletonRenderer;
|
||||||
|
import com.esotericsoftware.spine.android.SpineController;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface SpineControllerAfterPaintCallback {
|
||||||
|
void execute (SpineController controller, Canvas canvas, Array<SkeletonRenderer.RenderCommand> commands);
|
||||||
|
}
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
package com.esotericsoftware.spine.android.utils;
|
||||||
|
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
|
||||||
|
import com.esotericsoftware.spine.android.SkeletonRenderer;
|
||||||
|
import com.esotericsoftware.spine.android.SpineController;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface SpineControllerBeforePaintCallback {
|
||||||
|
void execute (SpineController controller, Canvas canvas);
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user