• RuntimesBugs
  • [Generic CPP Runtime, 4.2] No rendering happening when X scale is positive

Problem statement

Describe: 1) what you have tried, 2) what you expected, and 3) what actually happened.

Give step-by-step instructions so we can reproduce the problem, if possible.

Runtime information

Tell us your Spine Runtimes version.

Runtime 4.2

Hi! I've been using the Spine2D generic cpp runtime with raylib, a c++ game development framework.
I can get things to render fine, but there is a problem with the scale option.

What I Tried

I tried rendering the default spine boy project included with Spine.

Here is the setup code I used: (I left out the raylib-specific render code)

       atlas = std::make_unique<spine::Atlas>(util::getAssetPath("spine/spineboy-pro.atlas").c_str(), &textureLoaderSpine);
        skeletonJson = std::make_unique<spine::SkeletonJson>(atlas.get());
        skeletonJson->setScale(1);

        skeletonData = std::unique_ptr<spine::SkeletonData>(skeletonJson->readSkeletonDataFile(util::getAssetPath("spine/spineboy-pro.json").c_str()));

        // Create the spAnimationStateData
        animationStateData = std::unique_ptr<spine::AnimationStateData>(new spine::AnimationStateData(skeletonData.get()));

        // Set the default mix time between any pair of animations in seconds.
        animationStateData->setDefaultMix(0.1f);

        skeleton = std::unique_ptr<spine::Skeleton>(new spine::Skeleton(skeletonData.get()));

        skeleton->setX(400);
        skeleton->setY(400);

        skeleton->setScaleX(0.5);
        skeleton->setScaleY(0.5);

What I Expected

I expected the sprite to render exactly as it should, facing the right side and displaying a walking animation.

What I Got

When I set the scaleX to positive and the scaleY to positive, nothing renders at all.

        skeleton->setScaleX(0.5);
        skeleton->setScaleY(0.5);

But when I set either the scaleX or scaleY to negative, the animation renders just fine (I'm using the example animation from the spine boy project included with Spine).

        skeleton->setScaleX(-0.5);
        skeleton->setScaleY(0.5);

GIF example (x flipped, renders properly)

Related Discussions
...

It seems like you're encountering an issue where the Spine animation only renders when the scale is negative. This could be related to how the rendering is set up in your raylib integration or a potential issue with the Spine runtime handling of positive scale values. In the context provided, there are discussions about scale issues, such as the one where negative scale is used to flip characters, and how it might not render as expected when transitioning from negative to positive scale. This behavior is often by design to prevent unwanted effects like characters becoming paper-thin when interpolating between scales.

To troubleshoot, ensure that your rendering code correctly handles positive scale values and that there are no transformations or culling settings in raylib that might be affecting the visibility of the sprite. Additionally, you might want to check if there are any updates or patches for the Spine runtime that address this issue.

An additional bit of info:

when the X scale is positive, some of the uv values are NaN, as seen in these logs:

[2024-11-30 23:59:37.165] [debug] [spine_raylib.cpp:91] drawSkeleton: uv: 0.9172099, 0.19470699
[2024-11-30 23:59:37.165] [debug] [spine_raylib.cpp:91] drawSkeleton: uv: 0.84093875, 0.3837429
[2024-11-30 23:59:37.165] [debug] [spine_raylib.cpp:91] drawSkeleton: uv: 0.84093875, 0.52551985
[2024-11-30 23:59:37.165] [debug] [spine_raylib.cpp:91] drawSkeleton: uv: 0.7874837, 0.52551985
[2024-11-30 23:59:37.165] [debug] [spine_raylib.cpp:91] drawSkeleton: uv: 0.7874837, 0.3837429
[2024-11-30 23:59:37.165] [debug] [spine_raylib.cpp:91] drawSkeleton: uv: -nan, -nan
[2024-11-30 23:59:37.165] [debug] [spine_raylib.cpp:91] drawSkeleton: uv: -nan, -nan
[2024-11-30 23:59:37.165] [debug] [spine_raylib.cpp:91] drawSkeleton: uv: -nan, -nan
[2024-11-30 23:59:37.166] [debug] [spine_raylib.cpp:91] drawSkeleton: uv: -nan, -nan
[2024-11-30 23:59:37.166] [debug] [spine_raylib.cpp:91] drawSkeleton: uv: -nan, -nan
[2024-11-30 23:59:37.166] [debug] [spine_raylib.cpp:91] drawSkeleton: uv: -nan, -nan
[2024-11-30 23:59:37.166] [debug] [spine_raylib.cpp:91] drawSkeleton: uv: -nan, -nan

I've managed to make this work with another project file, but the spineboy example still does not work. I'll report back after trying again!

I've made my own sample project in the editor and tried importing it with Raylib. I'm getting the same problem where the sprite will not render if the X scale is positive.

I am completely stumped on how to proceed. May I have some help?

  • Змінено

This is indeed super weird. Can you share a full project I can try to run locally? Did you write your own renderer or are you using some Spine raylib integration from a 3rd party?

spine-cpp is heavily used by many people and companies. I can tentatively rule out, that something is broken in spine-cpp. The code is pretty similar to what we have in e.g. the spine-glfw example.

int main() {
	// Initialize GLFW and glbinding
	GLFWwindow *window = init_glfw();
	if (!window) return -1;

	// We use a y-down coordinate system, see renderer_set_viewport_size()
	Bone::setYDown(true);

	// Load the atlas and the skeleton data
	GlTextureLoader textureLoader;
	Atlas *atlas = new Atlas("data/spineboy-pma.atlas", &textureLoader);
	SkeletonBinary binary(atlas);
	SkeletonData *skeletonData = binary.readSkeletonDataFile("data/spineboy-pro.skel");

	// Create a skeleton from the data, set the skeleton's position to the bottom center of
	// the screen and scale it to make it smaller.
	Skeleton skeleton(skeletonData);
	skeleton.setPosition(width / 2, height - 100);
	skeleton.setScaleX(0.3);
	skeleton.setScaleY(0.3);

	// Create an AnimationState to drive animations on the skeleton. Set the "portal" animation
	// on track with index 0.
	AnimationStateData animationStateData(skeletonData);
	AnimationState animationState(&animationStateData);
	animationState.setAnimation(0, "walk", true);

	// Create the renderer and set the viewport size to match the window size. This sets up a
	// pixel perfect orthogonal projection for 2D rendering.
	renderer_t *renderer = renderer_create();
	renderer_set_viewport_size(renderer, width, height);

	// Rendering loop
	double lastTime = glfwGetTime();
	while (!glfwWindowShouldClose(window)) {
		// Calculate the delta time in seconds
		double currTime = glfwGetTime();
		float delta = currTime - lastTime;
		lastTime = currTime;

		// Update and apply the animation state to the skeleton
		animationState.update(delta);
		animationState.apply(skeleton);

		// Update the skeleton time (used for physics)
		skeleton.update(delta);

		// Calculate the new pose
		skeleton.updateWorldTransform(spine::Physics_Update);

		// Clear the screen
		gl::glClear(gl::GL_COLOR_BUFFER_BIT);

		// Render the skeleton in its current pose
		renderer_draw(renderer, &skeleton, true);

		// Present the rendering results and poll for events
		glfwSwapBuffers(window);
		glfwPollEvents();
	}

	// Dispose everything
	renderer_dispose(renderer);
	delete skeletonData;
	delete atlas;

	// Kill the window and GLFW
	glfwTerminate();
	return 0;
}

Which also uses the Spineboy skeleton.

Just for fun, I went through all scale combinations.

scaleX(0.3), scaleY(0.3)

scaleX(-0.3), scaleY(0.3)

scaleX(-0.3), scaleY(-0.3)

scaleX(0.3), scaleY(-0.3)

Note that for the last 2 variations, I had to set the skeleton's y position to be higher, otherwise the skeleton would go outside the screen at the bottom. Maybe that's what's happening for you as well? Doesn't explain the NaN uv coordinates though.