Skip to content

Commit

Permalink
✨ feat: 新增自定义视点位置选项
Browse files Browse the repository at this point in the history
  • Loading branch information
Nattsu39 committed Sep 11, 2024
1 parent 8a91842 commit 5105b32
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 41 deletions.
19 changes: 17 additions & 2 deletions src/cli/commands/export-spine-animation.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { CommandModule } from "yargs"
import { extractKeysToArray } from "@/utils.js";
import { extractKeysToArray, parseVector2 as parseVector2Arg } from "@/utils.js";
import { Exporters } from "@/exporter.js";
import { SpineAnimationExportOptions, exportSpineAnimation } from "@/handler.js";
import { KebabOptions, CommandOptions } from "../type-hind.js";
Expand Down Expand Up @@ -38,8 +38,23 @@ export default <CommandModule<CommandArguments, CommandArguments>>{
alias: "c",
type: "string",
default: null,
coerce: (arg) => {
if (typeof arg !== 'string') return
const vector2 = parseVector2Arg(arg)
return { width: vector2.x, height: vector2.y }
},
desc: "If set, old-style cropping is used, i.e. content that exceeds the canvas size will not be rendered. By default, AABB's min-max vertex positioning rendering range is used.",
},
"view-position": {
type: "string",
default: null,
coerce(arg) {
if (typeof arg !== 'string') return
const vector2 = parseVector2Arg(arg)
return { x: vector2.x, y: vector2.y }
},
desc: "Set the viewpoint manually. Useful when setting a smaller canvas size."
},
"selected-animation": {
alias: "s",
array: true,
Expand Down Expand Up @@ -73,7 +88,7 @@ export default <CommandModule<CommandArguments, CommandArguments>>{
type: "number",
default: 2,
desc: "Maximum number of concurrencies for export functions"
}
},
})
.example([
["$0 --export-type gif assets/", "Render assets in ./assets/ and export to GIF."],
Expand Down
23 changes: 9 additions & 14 deletions src/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,19 @@ import fs from "fs/promises";
import { createCanvas } from "node-canvas-webgl";
import { FfmpegFrameExporter } from "./exporter.js";
import { AssetPath, SpineRenderer, TexturePath, loadTexture } from "./renderer.js";
import { formatOutputPath, traverseDir, Viewsize } from "./utils.js";
import { formatOutputPath, traverseDir, ViewPosition, ViewSize } from "./utils.js";
import { TExporterType } from "./exporter.js";
import { AssetManager, ManagedWebGLRenderingContext } from "@node-spine-runtimes/webgl-3.8.99";
import { AssetManager, ManagedWebGLRenderingContext, Vector2 } from "@node-spine-runtimes/webgl-3.8.99";
import sharp from "sharp";
import path from "path";
import { formatString } from "./utils.js";

export function parseCanvasSize(size: string): Viewsize {
const canvasSize = size.split("x");
if (canvasSize.length !== 2) {
throw new Error("Canvas size format error! \n" + "Correct format: [width]x[height], for example 500x500.");
}
return { width: parseInt(canvasSize[0]), height: parseInt(canvasSize[1]) };
}

export interface SpineAnimationExportOptions {
outputPath?: string;
exportType: TExporterType;
exporterMaxConcurrent?: number;
canvasSize?: string | null;
canvasSize?: ViewSize;
viewPosition?: ViewPosition,
selectedAnimation?: string[];
preMultipliedAlpha: boolean;
scale?: number;
Expand All @@ -35,6 +28,7 @@ export async function exportSpineAnimation(inputDir: string, options: SpineAnima
exportType,
exporterMaxConcurrent = 2,
canvasSize,
viewPosition,
selectedAnimation = [],
preMultipliedAlpha = false,
fps = 30,
Expand Down Expand Up @@ -64,13 +58,14 @@ export async function exportSpineAnimation(inputDir: string, options: SpineAnima
if (selectedAnimation.length && !selectedAnimation.includes(animationName)) {
continue;
}
const viewsize = typeof canvasSize === "string" ? parseCanvasSize(canvasSize) : undefined;
const viewSize = canvasSize;
console.log(`${assetProcess}Start rendering the animation '${animationName}' of asset '${assetName}'...`);
const animationFrames = renderer.render({
skeleton,
state,
animationName,
viewsize,
viewSize,
viewPosition,
fps,
endPosition,
});
Expand All @@ -83,7 +78,7 @@ export async function exportSpineAnimation(inputDir: string, options: SpineAnima
{
outputPath: formatOutputPath(outputPath, formatObject),
fps,
autoCrop: viewsize !== undefined,
autoCrop: viewSize !== undefined,
}
);
}
Expand Down
34 changes: 11 additions & 23 deletions src/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import {
} from "@node-spine-runtimes/webgl-3.8.99";
import fs from "fs";
import path from "path";
import { sleep, replacePathSpecific, removePathExtension, Viewsize } from "./utils.js";
import { sleep, replacePathSpecific, removePathExtension, ViewSize, Viewport, ViewPosition } from "./utils.js";

export interface LoadedResult {
skeleton: Skeleton;
Expand All @@ -33,7 +33,8 @@ export interface LoadedResult {
export interface RenderOptions extends LoadedResult {
animationName?: string;
fps: number;
viewsize?: Viewsize;
viewSize?: ViewSize;
viewPosition?: ViewPosition;
endPosition?: number;
}

Expand Down Expand Up @@ -64,13 +65,6 @@ export class TimeKeeper {
}
}

export interface Viewport {
x: number;
y: number;
width: number;
height: number;
}

function calculateAnimationViewport(animation: Animation, skeleton: Skeleton, fps: number): Viewport {
skeleton.setToSetupPose();

Expand Down Expand Up @@ -164,34 +158,28 @@ export class SpineRenderer {
};

const render = () => {
this.renderer.camera.position.x = viewport.x + viewport.width / 2;
this.renderer.camera.position.y = viewport.y + viewport.height / 2;
this.renderer.camera.position.x = x_position;
this.renderer.camera.position.y = y_position;
this.gl.clearColor(0, 0, 0, 0);
this.gl.clear(this.gl.COLOR_BUFFER_BIT);
this.renderer.begin();
this.renderer.drawSkeleton(skeleton, true);
this.renderer.end();
};
let { skeleton, state, animationName = skeleton.data.animations[0].name, fps, viewsize, endPosition = Infinity } = options;
let { skeleton, state, animationName = skeleton.data.animations[0].name, fps, viewSize: viewsize, viewPosition, endPosition = Infinity } = options;

state.setAnimation(0, animationName, false);

const viewport = calculateAnimationViewport(skeleton.data.findAnimation(animationName)!, skeleton, fps);
if (viewsize === undefined) {
this.canvas.width = Math.round(viewport.width);
this.canvas.height = Math.round(viewport.height);
}
else {
this.canvas.width = viewsize.width;
this.canvas.height = viewsize.height;
}

this.canvas.width = viewsize?.width || Math.round(viewport.width);
this.canvas.height = viewsize?.height || Math.round(viewport.width)
if (this.canvas.width % 2 !== 0) this.canvas.width += 1;
if (this.canvas.height % 2 !== 0) this.canvas.height += 1;

const x_position = viewPosition?.x || viewport.x + viewport.width / 2
const y_position = viewPosition?.y || viewport.y + viewport.height / 2

let isComplete: boolean = false;
const renderingEnd = () => { isComplete = true };

state.addListener({
complete: renderingEnd,
});
Expand Down
22 changes: 20 additions & 2 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import fs from "fs/promises";
import { AssetPathGroup } from "./renderer.js";
import path from "path";
import { Vector2 } from "@node-spine-runtimes/webgl-3.8.99";

export const sleep = (waitTimeInMs: number) => new Promise((resolve) => setTimeout(resolve, waitTimeInMs));

Expand Down Expand Up @@ -90,7 +91,24 @@ export function defer<T = void>(): Deferred<T> {
return { resolve, reject, promise };
}

export interface Viewsize {
export interface ViewSize {
width: number;
height: number;
}
}

export interface ViewPosition {
x: number;
y: number;
}

export type Viewport = ViewSize & ViewPosition

export function parseVector2(arg: string): Vector2 {
arg = arg.replaceAll(`'`, '')
arg = arg.replaceAll(`"`, '')
const twoArgs = arg.split("x");
if (twoArgs.length !== 2) {
throw new Error("Arg format error! \n" + "Correct format: [arg1]x[arg2]");
}
return new Vector2(parseInt(twoArgs[0]), parseInt(twoArgs[1]))
}

0 comments on commit 5105b32

Please sign in to comment.