vtube-studio/services/geminiService.ts
James Twose 3eff403fb4 feat: Generate VTuber character sheet with expression assets
The Gemini service has been updated to generate a character sheet rather than a single avatar image. This sheet includes the main character and separate assets for closed eyes and an open mouth.

The `AvatarConfig` type and `RiggingEditor` component have been extended to handle these new expression assets (`textureClosedEye`, `textureOpenMouth`). A new `Sprite` component has been added to `Studio.tsx` to correctly render these specific regions from the generated character sheet. The UI has been updated to reflect the new generation process.
2025-11-20 20:55:47 +01:00

59 lines
1.8 KiB
TypeScript

import { GoogleGenAI } from "@google/genai";
/**
* Generates a VTuber avatar character sheet.
* Uses gemini-3-pro-image-preview for high quality.
*/
export const generateAvatarImage = async (description: string): Promise<string> => {
try {
// Initialize client inside the function to ensure we use the most up-to-date API key
const ai = new GoogleGenAI({ apiKey: process.env.API_KEY });
const prompt = `
Create a VTuber character sheet with a flat 2D anime style.
LAYOUT:
1. MAIN CHARACTER (Left side, takes up 70% of width):
- Front-facing view, head and shoulders.
- Neutral expression, eyes open, mouth closed.
2. EXPRESSION ASSETS (Right side, vertical column):
- Top: The same character's face with EYES CLOSED (for blinking).
- Bottom: The same character's face with MOUTH OPEN (for talking).
Character Description: ${description}
Style: Vibrant, clean lines, solid white or green background for easy keying.
`;
const response = await ai.models.generateContent({
model: 'gemini-3-pro-image-preview',
contents: {
parts: [
{ text: prompt }
]
},
config: {
imageConfig: {
aspectRatio: "16:9", // Wide to fit character sheet
imageSize: "1K"
}
}
});
// Parse response for image data
for (const part of response.candidates[0].content.parts) {
if (part.inlineData) {
const base64EncodeString = part.inlineData.data;
return `data:image/png;base64,${base64EncodeString}`;
}
}
throw new Error("No image data found in response");
} catch (error) {
console.error("Error generating avatar:", error);
throw error;
}
};