mirror of
https://github.com/isledecomp/isle.pizza.git
synced 2026-03-01 14:27:38 +00:00
Preload default textures for instant texture picker opening
Fetch and parse .tex files in the background when a textured part loads, and pass the results to TexturePickerModal as a prop. The modal no longer fetches on mount, eliminating the loading delay.
This commit is contained in:
parent
c3083b02af
commit
8adeb9fed6
@ -1,18 +1,17 @@
|
|||||||
<script>
|
<script>
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { parseTex } from '../../core/formats/TexParser.js';
|
|
||||||
import { quantizeImage, squareTexture } from '../../core/savegame/imageQuantizer.js';
|
import { quantizeImage, squareTexture } from '../../core/savegame/imageQuantizer.js';
|
||||||
import { saveCustomTexture, listCustomTextures, deleteCustomTexture } from '../../core/savegame/textureStorage.js';
|
import { saveCustomTexture, listCustomTextures, deleteCustomTexture } from '../../core/savegame/textureStorage.js';
|
||||||
import Carousel from '../Carousel.svelte';
|
import Carousel from '../Carousel.svelte';
|
||||||
|
|
||||||
export let textureInfo;
|
export let textureInfo;
|
||||||
export let palette = null;
|
export let palette = null;
|
||||||
|
export let defaults = null;
|
||||||
export let onSelect = () => {};
|
export let onSelect = () => {};
|
||||||
export let onClose = () => {};
|
export let onClose = () => {};
|
||||||
|
|
||||||
let defaults = [];
|
let defaultTextures = [];
|
||||||
let customTextures = [];
|
let customTextures = [];
|
||||||
let loadingDefaults = true;
|
|
||||||
let fileInput;
|
let fileInput;
|
||||||
let activeTab = 'default';
|
let activeTab = 'default';
|
||||||
let selectedCustomId = null;
|
let selectedCustomId = null;
|
||||||
@ -22,40 +21,20 @@
|
|||||||
let targetHeight = 128;
|
let targetHeight = 128;
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
await loadDefaults();
|
initDefaults();
|
||||||
await loadCustomTextures();
|
await loadCustomTextures();
|
||||||
});
|
});
|
||||||
|
|
||||||
async function loadDefaults() {
|
function initDefaults() {
|
||||||
loadingDefaults = true;
|
const source = defaults || [];
|
||||||
const loaded = [];
|
defaultTextures = source.map(tex => ({
|
||||||
|
...tex,
|
||||||
for (const texFile of textureInfo.texFiles) {
|
dataUrl: textureToDataUrl(tex)
|
||||||
try {
|
}));
|
||||||
const response = await fetch(`/${texFile}.tex`);
|
if (defaultTextures.length > 0) {
|
||||||
if (!response.ok) continue;
|
targetWidth = defaultTextures[0].width;
|
||||||
const buffer = await response.arrayBuffer();
|
targetHeight = defaultTextures[0].height;
|
||||||
const parsed = parseTex(buffer);
|
|
||||||
if (parsed.textures.length > 0) {
|
|
||||||
const tex = parsed.textures[0];
|
|
||||||
loaded.push({
|
|
||||||
name: texFile,
|
|
||||||
...tex,
|
|
||||||
dataUrl: textureToDataUrl(tex)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error(`Failed to load ${texFile}.tex:`, e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loaded.length > 0) {
|
|
||||||
targetWidth = loaded[0].width;
|
|
||||||
targetHeight = loaded[0].height;
|
|
||||||
}
|
|
||||||
|
|
||||||
defaults = loaded;
|
|
||||||
loadingDefaults = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadCustomTextures() {
|
async function loadCustomTextures() {
|
||||||
@ -127,7 +106,7 @@
|
|||||||
|
|
||||||
// Use the WDB palette (passed as prop) — this is the palette the game's
|
// Use the WDB palette (passed as prop) — this is the palette the game's
|
||||||
// DirectDraw surface actually uses. Fall back to first default's palette.
|
// DirectDraw surface actually uses. Fall back to first default's palette.
|
||||||
const targetPalette = palette || defaults[0]?.palette;
|
const targetPalette = palette || defaultTextures[0]?.palette;
|
||||||
if (!targetPalette) return;
|
if (!targetPalette) return;
|
||||||
|
|
||||||
const img = new Image();
|
const img = new Image();
|
||||||
@ -210,11 +189,8 @@
|
|||||||
|
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
{#if activeTab === 'default'}
|
{#if activeTab === 'default'}
|
||||||
{#if loadingDefaults}
|
|
||||||
<div class="loading">Loading textures...</div>
|
|
||||||
{:else}
|
|
||||||
<div class="texture-grid">
|
<div class="texture-grid">
|
||||||
{#each defaults as tex}
|
{#each defaultTextures as tex}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="texture-thumb"
|
class="texture-thumb"
|
||||||
@ -225,7 +201,6 @@
|
|||||||
</button>
|
</button>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
|
||||||
{:else}
|
{:else}
|
||||||
{#if customTextures.length > 0}
|
{#if customTextures.length > 0}
|
||||||
<div class="custom-carousel">
|
<div class="custom-carousel">
|
||||||
@ -333,13 +308,6 @@
|
|||||||
padding: 14px;
|
padding: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.loading {
|
|
||||||
color: var(--color-text-muted);
|
|
||||||
font-size: 0.85em;
|
|
||||||
text-align: center;
|
|
||||||
padding: 20px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.texture-grid {
|
.texture-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(2, 1fr);
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
|||||||
@ -12,6 +12,7 @@
|
|||||||
Act1PlaneIndices
|
Act1PlaneIndices
|
||||||
} from '../../core/savegame/constants.js';
|
} from '../../core/savegame/constants.js';
|
||||||
import { squareTexture } from '../../core/savegame/imageQuantizer.js';
|
import { squareTexture } from '../../core/savegame/imageQuantizer.js';
|
||||||
|
import { parseTex } from '../../core/formats/TexParser.js';
|
||||||
import NavButton from '../NavButton.svelte';
|
import NavButton from '../NavButton.svelte';
|
||||||
import ResetButton from '../ResetButton.svelte';
|
import ResetButton from '../ResetButton.svelte';
|
||||||
import EditorTooltip from '../EditorTooltip.svelte';
|
import EditorTooltip from '../EditorTooltip.svelte';
|
||||||
@ -45,6 +46,7 @@
|
|||||||
let showTextureModal = false;
|
let showTextureModal = false;
|
||||||
let texturePalette = null;
|
let texturePalette = null;
|
||||||
let wdbTexture = null;
|
let wdbTexture = null;
|
||||||
|
let preloadedDefaults = null;
|
||||||
|
|
||||||
// Current part info from flat list
|
// Current part info from flat list
|
||||||
$: currentEntry = allParts[globalIndex];
|
$: currentEntry = allParts[globalIndex];
|
||||||
@ -207,12 +209,36 @@
|
|||||||
// Load part with current color, textures, and parts map for shared LOD lookup
|
// Load part with current color, textures, and parts map for shared LOD lookup
|
||||||
renderer.loadPartWithColor(partRoi, currentColorValue, textures, partsMap)
|
renderer.loadPartWithColor(partRoi, currentColorValue, textures, partsMap)
|
||||||
loadedPartKey = partKey;
|
loadedPartKey = partKey;
|
||||||
|
|
||||||
|
// Preload default .tex files in background for the texture picker
|
||||||
|
if (textureInfo) {
|
||||||
|
preloadDefaultTextures(textureInfo);
|
||||||
|
} else {
|
||||||
|
preloadedDefaults = null;
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Failed to load part:', e);
|
console.error('Failed to load part:', e);
|
||||||
partError = e.message;
|
partError = e.message;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function preloadDefaultTextures(info) {
|
||||||
|
const loaded = [];
|
||||||
|
for (const texFile of info.texFiles) {
|
||||||
|
const response = await fetch(`/${texFile}.tex`);
|
||||||
|
if (!response.ok) continue;
|
||||||
|
const buffer = await response.arrayBuffer();
|
||||||
|
const parsed = parseTex(buffer);
|
||||||
|
if (parsed.textures.length > 0) {
|
||||||
|
loaded.push({ name: texFile, ...parsed.textures[0] });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Only apply if textureInfo hasn't changed since we started
|
||||||
|
if (textureInfo === info) {
|
||||||
|
preloadedDefaults = loaded;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function prevPart() {
|
function prevPart() {
|
||||||
globalIndex = globalIndex > 0 ? globalIndex - 1 : allParts.length - 1;
|
globalIndex = globalIndex > 0 ? globalIndex - 1 : allParts.length - 1;
|
||||||
loadedPartKey = null;
|
loadedPartKey = null;
|
||||||
@ -358,6 +384,7 @@
|
|||||||
<TexturePickerModal
|
<TexturePickerModal
|
||||||
{textureInfo}
|
{textureInfo}
|
||||||
palette={texturePalette}
|
palette={texturePalette}
|
||||||
|
defaults={preloadedDefaults}
|
||||||
onSelect={handleTextureSelect}
|
onSelect={handleTextureSelect}
|
||||||
onClose={() => showTextureModal = false}
|
onClose={() => showTextureModal = false}
|
||||||
/>
|
/>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user