feat: Add Flip X and Flip Y options for UV baking
Build and Deploy / build-and-push (push) Successful in 1m56s
Build and Deploy / build-and-push (push) Successful in 1m56s
This commit is contained in:
@@ -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
@@ -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
@@ -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>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user