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.PlayPause,
|
||||
Destination.AnimationStateEvents,
|
||||
Destination.DebugRendering,
|
||||
Destination.IKFollowing,
|
||||
Destination.Physics
|
||||
),
|
||||
@ -87,6 +88,12 @@ fun AppContent() {
|
||||
AnimationState(navController)
|
||||
}
|
||||
|
||||
composable(
|
||||
Destination.DebugRendering.route
|
||||
) {
|
||||
DebugRendering(navController)
|
||||
}
|
||||
|
||||
composable(
|
||||
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 SimpleAnimation : Destination("simpleAnimation", "Simple Animation")
|
||||
data object PlayPause : Destination("playPause", "Play/Pause")
|
||||
data object DebugRendering: Destination("debugRendering", "Debug Renderer")
|
||||
data object AnimationStateEvents : Destination("animationStateEvents", "Animation State Listener")
|
||||
data object IKFollowing : Destination("ikFollowing", "IK Following")
|
||||
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;
|
||||
}
|
||||
|
||||
public void render (Canvas canvas, Skeleton skeleton) {
|
||||
Array<RenderCommand> commands = render(skeleton);
|
||||
public void render (Canvas canvas, Array<RenderCommand> commands) {
|
||||
for (int i = 0; i < commands.size; i++) {
|
||||
RenderCommand command = commands.get(i);
|
||||
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;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Point;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import com.esotericsoftware.spine.AnimationState;
|
||||
import com.esotericsoftware.spine.AnimationStateData;
|
||||
import com.esotericsoftware.spine.Skeleton;
|
||||
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;
|
||||
|
||||
public class SpineController {
|
||||
@ -16,6 +20,8 @@ public class SpineController {
|
||||
private SpineControllerCallback onInitialized;
|
||||
private SpineControllerCallback onBeforeUpdateWorldTransforms;
|
||||
private SpineControllerCallback onAfterUpdateWorldTransforms;
|
||||
private SpineControllerBeforePaintCallback onBeforePaint;
|
||||
private SpineControllerAfterPaintCallback onAfterPaint;
|
||||
|
||||
public Builder setOnInitialized(SpineControllerCallback onInitialized) {
|
||||
this.onInitialized = onInitialized;
|
||||
@ -32,11 +38,23 @@ public class SpineController {
|
||||
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() {
|
||||
SpineController spineController = new SpineController();
|
||||
spineController.onInitialized = onInitialized;
|
||||
spineController.onBeforeUpdateWorldTransforms = onBeforeUpdateWorldTransforms;
|
||||
spineController.onAfterUpdateWorldTransforms = onAfterUpdateWorldTransforms;
|
||||
spineController.onBeforePaint = onBeforePaint;
|
||||
spineController.onAfterPaint = onAfterPaint;
|
||||
return spineController;
|
||||
}
|
||||
}
|
||||
@ -44,6 +62,8 @@ public class SpineController {
|
||||
private @Nullable SpineControllerCallback onInitialized;
|
||||
private @Nullable SpineControllerCallback onBeforeUpdateWorldTransforms;
|
||||
private @Nullable SpineControllerCallback onAfterUpdateWorldTransforms;
|
||||
private @Nullable SpineControllerBeforePaintCallback onBeforePaint;
|
||||
private @Nullable SpineControllerAfterPaintCallback onAfterPaint;
|
||||
private AndroidSkeletonDrawable drawable;
|
||||
private boolean playing = true;
|
||||
private double offsetX = 0;
|
||||
@ -83,7 +103,7 @@ public class SpineController {
|
||||
return drawable.getAnimationState();
|
||||
}
|
||||
|
||||
AndroidSkeletonDrawable getDrawable() {
|
||||
public AndroidSkeletonDrawable getDrawable() {
|
||||
if (drawable == null) throw new RuntimeException("Controller is not initialized yet.");
|
||||
return drawable;
|
||||
}
|
||||
@ -128,4 +148,16 @@ public class SpineController {
|
||||
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;
|
||||
|
||||
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.Bounds;
|
||||
import com.esotericsoftware.spine.android.bounds.BoundsProvider;
|
||||
@ -153,11 +153,15 @@ public class SpineView extends View implements Choreographer.FrameCallback {
|
||||
}
|
||||
|
||||
canvas.save();
|
||||
|
||||
canvas.translate(offsetX, offsetY);
|
||||
canvas.scale(scaleX, scaleY * -1);
|
||||
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();
|
||||
}
|
||||
|
||||
@ -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