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.
59 lines
1.8 KiB
TypeScript
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;
|
|
}
|
|
};
|