Tighten camera and pose diversity for generation prompts

This commit is contained in:
2026-06-18 01:19:34 +09:00
parent 0ec024fe54
commit 39d18aadb7

View File

@@ -63,7 +63,7 @@ function damageSpec(stage) {
baseLayerDamage: baseLayerDamage:
"no base-layer damage yet; support underlayers, bodysuit panels, and lower coverage panels are intact before combat", "no base-layer damage yet; support underlayers, bodysuit panels, and lower coverage panels are intact before combat",
camera: camera:
"low-angle side-front or three-quarter side-forward view for clear character identity without flat frontal dominance", "low-angle three-quarter side-forward view to keep identity clear without head-on framing",
cameraConstraint: cameraConstraint:
"camera choice should include side/rear/low/high rotations across the deck; do not use direct frontal framing.", "camera choice should include side/rear/low/high rotations across the deck; do not use direct frontal framing.",
}, },
@@ -351,11 +351,41 @@ const CAMERA_ORIENTATION_RULES = {
}; };
const CAMERA_STAGE_REQUIREMENTS = { const CAMERA_STAGE_REQUIREMENTS = {
normal: { 1: "high", 2: "low" }, normal: {
"light-damage": { 1: "high", 2: "low" }, 0: "side",
"medium-damage": { 1: "high", 2: "low" }, 1: "high",
"heavy-damage": { 1: "high", 2: "low" }, 2: "low",
defeated: { 1: "high", 2: "low" }, 3: "rear",
4: "side",
},
"light-damage": {
0: "low",
1: "high",
2: "side",
3: "rear",
4: "high",
},
"medium-damage": {
0: "side",
1: "low",
2: "high",
3: "rear",
4: "side",
},
"heavy-damage": {
0: "low",
1: "high",
2: "rear",
3: "side",
4: "high",
},
defeated: {
0: "rear",
1: "low",
2: "high",
3: "side",
4: "rear",
},
}; };
function templateOrientation(spec, character, stage) { function templateOrientation(spec, character, stage) {
@@ -667,47 +697,26 @@ function poseFamily(phrase) {
function pickCameraForStage(spec, character, stage) { function pickCameraForStage(spec, character, stage) {
const orientationPlan = CAMERA_ORIENTATION_PLAN[spec.label] ?? CAMERA_ORIENTATION_PLAN.normal; const orientationPlan = CAMERA_ORIENTATION_PLAN[spec.label] ?? CAMERA_ORIENTATION_PLAN.normal;
const orientationSeed = charSeed(`${character.id}-${character.scene}-${spec.label}`);
const preferredTemplateOrientation = templateOrientation(spec, character, stage);
const requiredOrientation = CAMERA_STAGE_REQUIREMENTS[spec.label]?.[stage]; const requiredOrientation = CAMERA_STAGE_REQUIREMENTS[spec.label]?.[stage];
const requiredTemplateOrientation = templateOrientation(spec, character, stage);
const historyKey = character.id; const historyKey = character.id;
const history = cameraHistoryByCharacter.get(historyKey) ?? []; const history = cameraHistoryByCharacter.get(historyKey) ?? [];
let resolvedIndex = firstIndexForOrientation( const firstTemplateIndex = firstIndexForOrientation(
orientationPlan, orientationPlan,
preferredTemplateOrientation, requiredTemplateOrientation,
); );
let resolvedIndex = firstTemplateIndex === -1 ? (stage % orientationPlan.length) : firstTemplateIndex;
if (resolvedIndex === -1) { if (resolvedIndex === -1) {
resolvedIndex = (orientationSeed + stage) % orientationPlan.length; resolvedIndex = stage % orientationPlan.length;
} }
const tailNeedOrientation = requiredOrientation ?? (() => {
if (stage === 4 && !history.includes("rear")) {
const tailSeed = charSeed(`${character.id}-${spec.label}-tail`);
return tailSeed % 2 === 0 ? "rear" : "side";
}
return null;
})();
if (requiredOrientation) { const requiredIndex = requiredOrientation
const requiredIndex = firstIndexForOrientation(orientationPlan, requiredOrientation); ? firstIndexForOrientation(orientationPlan, requiredOrientation)
if (requiredIndex !== -1 && !history.includes(requiredOrientation)) { : -1;
resolvedIndex = requiredIndex; if (requiredIndex !== -1) {
} resolvedIndex = requiredIndex;
} else if (tailNeedOrientation) {
const tailOrientationIndex = firstIndexForOrientation(orientationPlan, tailNeedOrientation);
if (tailOrientationIndex !== -1) {
resolvedIndex = tailOrientationIndex;
}
} else if (!history.includes("low") && stage >= 2) {
const fallbackLowIndex = firstIndexForOrientation(orientationPlan, "low");
if (fallbackLowIndex !== -1 && !history.includes("low")) {
resolvedIndex = fallbackLowIndex;
}
} else if (!history.includes("high") && stage >= 1) {
const fallbackHighIndex = firstIndexForOrientation(orientationPlan, "high");
if (fallbackHighIndex !== -1 && !history.includes("high")) {
resolvedIndex = fallbackHighIndex;
}
} }
if (history.length > 0 && orientationPlan[resolvedIndex] === history[history.length - 1]) { if (history.length > 0 && orientationPlan[resolvedIndex] === history[history.length - 1]) {
resolvedIndex = nextDifferentIndex( resolvedIndex = nextDifferentIndex(
orientationPlan, orientationPlan,
@@ -788,9 +797,15 @@ function pickPoseModifier(spec, character, stage, orientation) {
const history = poseHistoryByCharacter.get(character.id) ?? []; const history = poseHistoryByCharacter.get(character.id) ?? [];
let selected = selectedCandidate; let selected = selectedCandidate;
if (history.length > 0 && selected === history[history.length - 1]) { if (history.length > 0 && history.slice(-3).includes(selected)) {
const fallbackIndex = (finalPool.indexOf(selected) + 1) % finalPool.length; for (let shift = 1; shift < finalPool.length; shift += 1) {
selected = finalPool[fallbackIndex]; const fallbackIndex = (index + shift) % finalPool.length;
const fallbackPose = finalPool[fallbackIndex];
if (!history.slice(-3).includes(fallbackPose)) {
selected = fallbackPose;
break;
}
}
} }
const family = poseFamily(selected); const family = poseFamily(selected);
@@ -827,15 +842,15 @@ function promptFor(character, stage) {
"broad-damage gate: if the damaged outfit mostly reads as many fine cracks, tiny scratches, lace frays, thorny threads, or vine-like tendrils, the card fails even when the character is exposed. Each major damaged zone should include at least one large missing panel, wide split seam, snapped fastener, sheared plate, or heavy hanging remnant that can be understood at card size."; "broad-damage gate: if the damaged outfit mostly reads as many fine cracks, tiny scratches, lace frays, thorny threads, or vine-like tendrils, the card fails even when the character is exposed. Each major damaged zone should include at least one large missing panel, wide split seam, snapped fastener, sheared plate, or heavy hanging remnant that can be understood at card size.";
const camera = pickCameraForStage(spec, character, stage); const camera = pickCameraForStage(spec, character, stage);
const angleControl = const angleControl =
"Angle strictness: this render must not use frontal/front-facing composition. Use side/rear/low/high only, and explicitly rotate camera family between stages."; "Angle hard lock: render only side/rear/low/high compositions. Do not allow a full frontal/head-on framing under any circumstance.";
const angleConstraint = const angleConstraint =
"Do not output front-facing framing for default action shots. Use side/rear/low/high angles as primary composition, and rotate the view family between shots so the same character does not repeat the exact same camera axis."; "Do not output front-facing framing for any combat shot. If the first draft is front-facing, it is rejected and must be regenerated.";
const angleHardStop = const angleHardStop =
"Hard stop: frontal, direct eye-level, or symmetric head-on compositions are disallowed for combat coverage."; "Hard stop: frontal, direct eye-level, or symmetrical front-on compositions are disallowed for combat coverage.";
const poseDiversityConstraint = const poseDiversityConstraint =
"Pose diversity is mandatory: rotate through guard, movement, recoil, evasive, grounded, and collapse actions across the five stages; do not repeat the same motion family in multiple consecutive shots."; "Pose diversity is mandatory: rotate through guard, movement, recoil, evasive, grounded, and collapse actions across the five stages; do not repeat the same motion family in multiple consecutive shots.";
const poseHardStop = const poseHardStop =
"Hard stop: do not keep the exact same pose family or core action type in repeated stages; distribute at least three different families over five stages."; "Hard stop: do not keep the exact same pose family or core action type in repeated stages; distribute at least three different families over five stages, with low-angle/high-angle poses represented in light or higher damage stages.";
const poseModifier = pickPoseModifier(spec, character, stage, camera.orientation); const poseModifier = pickPoseModifier(spec, character, stage, camera.orientation);
return [ return [
"Use case: stylized-concept", "Use case: stylized-concept",