feat: Add Flip X and Flip Y options for UV baking
Build and Deploy / build-and-push (push) Successful in 1m56s

This commit is contained in:
AI Bot
2026-09-17 09:44:35 +05:30
parent 12e406664b
commit 6e182cf4ac
3 changed files with 45 additions and 20 deletions
+2 -2
View File
@@ -7,7 +7,7 @@ import { useTexturePipeline } from "../hooks/useTexturePipeline";
interface ModelViewerProps {
object: THREE.Object3D | null;
sceneObjects: any[];
bakeRequest?: { id: string, type: 'planar' | 'cylinder' | 'sphere' | 'box', timestamp: number } | null;
bakeRequest?: { id: string, type: 'planar' | 'cylinder' | 'sphere' | 'box', flipX?: boolean, flipY?: boolean, timestamp: number } | null;
onBakeComplete?: (dataUrl: string) => void;
}
@@ -271,7 +271,7 @@ export const ModelViewer = forwardRef<THREE.Group, ModelViewerProps>(({ object,
// Bake
try {
const dataUrl = bakeTextureToNewUVs(targetMesh, oldUVArray, tex, gl, 1024);
const dataUrl = bakeTextureToNewUVs(targetMesh, oldUVArray, tex, gl, 1024, bakeRequest.flipX, bakeRequest.flipY);
onBakeComplete(dataUrl);
} catch (e) {
console.error("Baking failed", e);
+13 -4
View File
@@ -100,7 +100,9 @@ export function bakeTextureToNewUVs(
oldUVArray: Float32Array,
oldTexture: THREE.Texture,
renderer: THREE.WebGLRenderer,
resolution = 1024
resolution = 1024,
flipX = false,
flipY = false
): string {
// Store original geometry attributes
const geometry = mesh.geometry.clone();
@@ -113,15 +115,22 @@ export function bakeTextureToNewUVs(
// Color is sampled from the old texture using the old UVs (uv2)
const bakeMaterial = new THREE.ShaderMaterial({
uniforms: {
tDiffuse: { value: oldTexture }
tDiffuse: { value: oldTexture },
uFlipX: { value: flipX ? 1.0 : 0.0 },
uFlipY: { value: flipY ? 1.0 : 0.0 }
},
vertexShader: `
attribute vec2 uv2;
varying vec2 vOldUV;
uniform float uFlipX;
uniform float uFlipY;
void main() {
vOldUV = uv2;
// Flip X and Y to fix upside down and reversed texture
gl_Position = vec4(1.0 - uv.x * 2.0, 1.0 - uv.y * 2.0, 0.0, 1.0);
float finalX = mix(uv.x * 2.0 - 1.0, 1.0 - uv.x * 2.0, uFlipX);
float finalY = mix(uv.y * 2.0 - 1.0, 1.0 - uv.y * 2.0, uFlipY);
gl_Position = vec4(finalX, finalY, 0.0, 1.0);
}
`,
fragmentShader: `
+19 -3
View File
@@ -642,7 +642,8 @@ export default function Editor() {
const [renderProgress, setRenderProgress] = useState<{ step: string; progress: number } | null>(null);
const [isRenderComplete, setIsRenderComplete] = useState(false);
const [bakedDataUrl, setBakedDataUrl] = useState<string | null>(null);
const [bakeRequest, setBakeRequest] = useState<{ id: string, type: 'planar' | 'cylinder' | 'sphere' | 'box', timestamp: number } | null>(null);
const [bakeOptions, setBakeOptions] = useState({ flipX: false, flipY: false });
const [bakeRequest, setBakeRequest] = useState<{ id: string, type: 'planar' | 'cylinder' | 'sphere' | 'box', flipX: boolean, flipY: boolean, timestamp: number } | null>(null);
const [importStatus, setImportStatus] = useState<string | null>(null);
const [importError, setImportError] = useState<string | null>(null);
const [modelData, setModelData] = useState<{ name: string, data: string, extension: string } | null>(null);
@@ -4687,19 +4688,34 @@ export default function Editor() {
<option value="triplanar">Triplanar</option>
</select>
{selectedObject.materialProps?.mappingType && selectedObject.materialProps.mappingType !== 'uv' && selectedObject.materialProps.mappingType !== 'triplanar' && (
<div className="flex flex-col gap-2 mt-2 bg-zinc-800/50 p-2 rounded border border-zinc-700/50">
<span className="text-[10px] text-zinc-400 font-bold mb-1">Baking Options</span>
<div className="flex gap-4">
<label className="flex items-center gap-1.5 cursor-pointer">
<input type="checkbox" checked={bakeOptions.flipX} onChange={e => setBakeOptions(p => ({...p, flipX: e.target.checked}))} className="rounded bg-zinc-900 border-zinc-700 text-blue-500 w-3 h-3" />
<span className="text-[10px] text-zinc-300">Flip X</span>
</label>
<label className="flex items-center gap-1.5 cursor-pointer">
<input type="checkbox" checked={bakeOptions.flipY} onChange={e => setBakeOptions(p => ({...p, flipY: e.target.checked}))} className="rounded bg-zinc-900 border-zinc-700 text-blue-500 w-3 h-3" />
<span className="text-[10px] text-zinc-300">Flip Y</span>
</label>
</div>
<button
onClick={() => {
if (!selectedObject.materialProps?.map) return;
setBakeRequest({
id: selectedObject.id,
type: selectedObject.materialProps.mappingType as any,
flipX: bakeOptions.flipX,
flipY: bakeOptions.flipY,
timestamp: Date.now()
});
}}
className="w-full py-2 bg-orange-500 hover:bg-orange-600 text-white font-bold rounded shadow transition-colors mt-1 text-[11px]"
className="w-full py-2 bg-orange-500 hover:bg-orange-600 text-white font-bold rounded shadow transition-colors text-[11px] mt-1"
>
{bakeRequest?.id === selectedObject.id ? 'Baking...' : 'Bake to UVs (Required for 2D AI)'}
{bakeRequest?.id === selectedObject.id ? 'Baking...' : 'Bake to UVs (For 2D AI)'}
</button>
</div>
)}
</div>