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 { interface ModelViewerProps {
object: THREE.Object3D | null; object: THREE.Object3D | null;
sceneObjects: any[]; 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; onBakeComplete?: (dataUrl: string) => void;
} }
@@ -271,7 +271,7 @@ export const ModelViewer = forwardRef<THREE.Group, ModelViewerProps>(({ object,
// Bake // Bake
try { try {
const dataUrl = bakeTextureToNewUVs(targetMesh, oldUVArray, tex, gl, 1024); const dataUrl = bakeTextureToNewUVs(targetMesh, oldUVArray, tex, gl, 1024, bakeRequest.flipX, bakeRequest.flipY);
onBakeComplete(dataUrl); onBakeComplete(dataUrl);
} catch (e) { } catch (e) {
console.error("Baking failed", e); console.error("Baking failed", e);
+13 -4
View File
@@ -100,7 +100,9 @@ export function bakeTextureToNewUVs(
oldUVArray: Float32Array, oldUVArray: Float32Array,
oldTexture: THREE.Texture, oldTexture: THREE.Texture,
renderer: THREE.WebGLRenderer, renderer: THREE.WebGLRenderer,
resolution = 1024 resolution = 1024,
flipX = false,
flipY = false
): string { ): string {
// Store original geometry attributes // Store original geometry attributes
const geometry = mesh.geometry.clone(); const geometry = mesh.geometry.clone();
@@ -113,15 +115,22 @@ export function bakeTextureToNewUVs(
// Color is sampled from the old texture using the old UVs (uv2) // Color is sampled from the old texture using the old UVs (uv2)
const bakeMaterial = new THREE.ShaderMaterial({ const bakeMaterial = new THREE.ShaderMaterial({
uniforms: { uniforms: {
tDiffuse: { value: oldTexture } tDiffuse: { value: oldTexture },
uFlipX: { value: flipX ? 1.0 : 0.0 },
uFlipY: { value: flipY ? 1.0 : 0.0 }
}, },
vertexShader: ` vertexShader: `
attribute vec2 uv2; attribute vec2 uv2;
varying vec2 vOldUV; varying vec2 vOldUV;
uniform float uFlipX;
uniform float uFlipY;
void main() { void main() {
vOldUV = uv2; 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: ` fragmentShader: `
+30 -14
View File
@@ -642,7 +642,8 @@ export default function Editor() {
const [renderProgress, setRenderProgress] = useState<{ step: string; progress: number } | null>(null); const [renderProgress, setRenderProgress] = useState<{ step: string; progress: number } | null>(null);
const [isRenderComplete, setIsRenderComplete] = useState(false); const [isRenderComplete, setIsRenderComplete] = useState(false);
const [bakedDataUrl, setBakedDataUrl] = useState<string | null>(null); 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 [importStatus, setImportStatus] = useState<string | null>(null);
const [importError, setImportError] = useState<string | null>(null); const [importError, setImportError] = useState<string | null>(null);
const [modelData, setModelData] = useState<{ name: string, data: string, extension: 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> <option value="triplanar">Triplanar</option>
</select> </select>
{selectedObject.materialProps?.mappingType && selectedObject.materialProps.mappingType !== 'uv' && selectedObject.materialProps.mappingType !== 'triplanar' && ( {selectedObject.materialProps?.mappingType && selectedObject.materialProps.mappingType !== 'uv' && selectedObject.materialProps.mappingType !== 'triplanar' && (
<button <div className="flex flex-col gap-2 mt-2 bg-zinc-800/50 p-2 rounded border border-zinc-700/50">
onClick={() => { <span className="text-[10px] text-zinc-400 font-bold mb-1">Baking Options</span>
if (!selectedObject.materialProps?.map) return; <div className="flex gap-4">
setBakeRequest({ <label className="flex items-center gap-1.5 cursor-pointer">
id: selectedObject.id, <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" />
type: selectedObject.materialProps.mappingType as any, <span className="text-[10px] text-zinc-300">Flip X</span>
timestamp: Date.now() </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" />
className="w-full py-2 bg-orange-500 hover:bg-orange-600 text-white font-bold rounded shadow transition-colors mt-1 text-[11px]" <span className="text-[10px] text-zinc-300">Flip Y</span>
> </label>
{bakeRequest?.id === selectedObject.id ? 'Baking...' : 'Bake to UVs (Required for 2D AI)'} </div>
</button> <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 text-[11px] mt-1"
>
{bakeRequest?.id === selectedObject.id ? 'Baking...' : 'Bake to UVs (For 2D AI)'}
</button>
</div>
)} )}
</div> </div>