Skip to content

How can I render the same model instance to multiple targets? #30

@TheAyes

Description

@TheAyes

Hello!

I have a rather specific issue. I'm building my own dashboard for live2d and need to view the same model instance in multiple places.
This is mainly my dashboard with all the settings, and my overlay for streaming in OBS.
However when opening the overlay (or another dashboard instance) it initializes a completely new model instance. This makes it play different animations and doesn't represent the actual visible model.

Image

My goal is to create a single model renderer and then simply being able to view the rendered output in multiple places. Is this possible?

P.S.: Considering there's no React example provided anywhere I kinda had to "interpret" the vue example. I would actually prefer to use @pixi/react but can't figure out how to use it at all.

P.P.S.:

Details

Here's my code
import { Live2DSprite } from "easy-live2d";
import { Application, Ticker } from "pixi.js";
import { type FC, useEffect, useId, useRef } from "react";
import styles from "./model.module.css";

type ModelProps = {
	wsUrl?: string;
};

export const Model: FC<ModelProps> = () => {
	const spriteRef = useRef<Live2DSprite>(null);
	const canvasRef = useRef<HTMLCanvasElement>(null);
	const canvasId = useId();

	useEffect(() => {
		if (!canvasRef || !canvasRef.current) return;
		const canvas = canvasRef.current;
		const parent = canvas.parentElement;
		if (!parent) return;

		let isCleaningUp = false;
		const app = new Application();

		const initPixi = async () => {
			if (isCleaningUp) return;

			await app.init({
				backgroundAlpha: 0,
				canvas: canvas,
				width: parent.clientWidth,
				height: parent.clientHeight,
				resizeTo: parent, // Auto-resize to parent
			});

			const live2dSprite = new Live2DSprite({
				modelPath: "/Cubism/Resources/Hiyori/Hiyori.model3.json",
				ticker: Ticker.shared,
			});

			spriteRef.current = live2dSprite;

			const sizeMultiplier = 1;
			live2dSprite.anchor.set(0.5, 1);
			live2dSprite.setSize(
				canvas.width * window.devicePixelRatio * sizeMultiplier,
				canvas.height * window.devicePixelRatio * sizeMultiplier,
			);
			live2dSprite.position.set(canvas.width / 32, 0);

			app.stage.addChild(live2dSprite);
		};

		initPixi().catch(console.error);

		return () => {
			isCleaningUp = true;

			// Destroy sprite first, before app
			if (spriteRef.current) {
				try {
					spriteRef.current.destroy();
				} catch (error) {
					console.warn("Error destroying Live2D sprite:", error);
				}
				spriteRef.current = null;
			}

			// Then destroy the PIXI app
			if (app) {
				try {
					app.destroy(true, {
						children: true,
						texture: true,
						textureSource: true,
						style: true,
						context: true,
					});
				} catch (error) {
					console.warn("Error destroying PIXI app:", error);
				}
			}
		};
	}, []);

	return <canvas className={styles.model} ref={canvasRef} id={canvasId} />;
};

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions