mirror of
https://github.com/BinomialLLC/basis_universal.git
synced 2026-06-08 08:33:53 +00:00
adding mipmap options, tex array layer/cubemap index/mipmap level selection, file size display, display zooming, bilinear checkbox, other UI tweaks/fixes
This commit is contained in:
@@ -16,13 +16,14 @@
|
||||
|
||||
function log(s)
|
||||
{
|
||||
// var div = document.createElement('div');
|
||||
// div.innerHTML = s;
|
||||
// document.getElementById('log-panel').appendChild(div);
|
||||
|
||||
var panel = document.getElementById('log-panel');
|
||||
|
||||
if (panel.childElementCount >= 750)
|
||||
panel.innerHTML = '';
|
||||
|
||||
var div = document.createElement('div');
|
||||
div.innerHTML = s;
|
||||
document.getElementById('log-panel').appendChild(div);
|
||||
panel.appendChild(div);
|
||||
}
|
||||
|
||||
function logClear()
|
||||
@@ -256,6 +257,7 @@
|
||||
var ldrHDRUpconversionScale = 1.0;
|
||||
var linearToSRGBFlag = false;
|
||||
var drawUseBilinearFiltering = false;
|
||||
var displayZoom = 1.0;
|
||||
|
||||
var tex, width, height, is_hdr, layers, levels, faces, tex_has_alpha, tex_is_srgb;
|
||||
var alignedWidth, alignedHeight, format, displayWidth, displayHeight;
|
||||
@@ -272,6 +274,22 @@
|
||||
if (!width)
|
||||
return;
|
||||
|
||||
// Keep the WebGL buffer at native resolution; use CSS to zoom the display.
|
||||
var canvas = elem('canvas');
|
||||
if (canvas.width !== displayWidth || canvas.height !== displayHeight)
|
||||
{
|
||||
canvas.width = displayWidth;
|
||||
canvas.height = displayHeight;
|
||||
}
|
||||
|
||||
var cssW = Math.round(displayWidth * displayZoom);
|
||||
var cssH = Math.round(displayHeight * displayZoom);
|
||||
canvas.style.width = cssW + 'px';
|
||||
canvas.style.height = cssH + 'px';
|
||||
|
||||
// Match CSS upscaling to the bilinear filtering setting.
|
||||
canvas.style.imageRendering = drawUseBilinearFiltering ? 'auto' : 'pixelated';
|
||||
|
||||
renderer.drawTexture(tex, displayWidth, displayHeight, drawMode, drawScale * ldrHDRUpconversionScale, linearToSRGBFlag, drawUseBilinearFiltering);
|
||||
}
|
||||
|
||||
@@ -460,8 +478,8 @@
|
||||
return;
|
||||
}
|
||||
|
||||
width = ktx2File.getWidth();
|
||||
height = ktx2File.getHeight();
|
||||
var baseWidth = ktx2File.getWidth();
|
||||
var baseHeight = ktx2File.getHeight();
|
||||
var srcBlockWidth = ktx2File.getBlockWidth();
|
||||
var srcBlockHeight = ktx2File.getBlockHeight();
|
||||
|
||||
@@ -478,6 +496,48 @@
|
||||
tex_has_alpha = ktx2File.getHasAlpha();
|
||||
tex_is_srgb = ktx2File.isSRGB();
|
||||
|
||||
updateMipmapSliderRange(levels);
|
||||
|
||||
// Read the desired mipmap level from the slider, validate, and clamp if needed.
|
||||
var selectedLevel = parseInt(document.getElementById('mipmap-level-slider').value, 10);
|
||||
if (isNaN(selectedLevel) || selectedLevel < 0 || selectedLevel >= levels)
|
||||
{
|
||||
selectedLevel = 0;
|
||||
document.getElementById('mipmap-level-slider').value = 0;
|
||||
document.getElementById('mipmap-level-value').textContent = '0';
|
||||
}
|
||||
|
||||
updateCubemapFaceRange(faces);
|
||||
|
||||
// Read the desired cubemap face from the slider, validate, and clamp if needed.
|
||||
var selectedFace = parseInt(document.getElementById('cubemap-face-slider').value, 10);
|
||||
if (isNaN(selectedFace) || selectedFace < 0 || selectedFace >= faces)
|
||||
{
|
||||
selectedFace = 0;
|
||||
document.getElementById('cubemap-face-slider').value = 0;
|
||||
document.getElementById('cubemap-face-value').textContent = '0';
|
||||
}
|
||||
|
||||
// For array textures, layers==0 means non-arrayed (treat as 1 layer at index 0).
|
||||
var effectiveLayers = Math.max(1, layers);
|
||||
updateArrayLayerRange(effectiveLayers);
|
||||
|
||||
// Read the desired array layer from the slider, validate, and clamp if needed.
|
||||
var selectedLayer = parseInt(document.getElementById('array-layer-slider').value, 10);
|
||||
if (isNaN(selectedLayer) || selectedLayer < 0 || selectedLayer >= effectiveLayers)
|
||||
{
|
||||
selectedLayer = 0;
|
||||
document.getElementById('array-layer-slider').value = 0;
|
||||
document.getElementById('array-layer-value').textContent = '0';
|
||||
document.getElementById('array-layer-input').value = 0;
|
||||
}
|
||||
|
||||
// Set width/height to the selected mip level's original (unpadded) dimensions.
|
||||
// This way all downstream code (alignment, transcoding, texture creation) works unchanged.
|
||||
var imageLevelInfo = ktx2File.getImageLevelInfo(selectedLevel, selectedLayer, selectedFace);
|
||||
width = imageLevelInfo.origWidth;
|
||||
height = imageLevelInfo.origHeight;
|
||||
|
||||
// If a HDR KTX2 file was upconverted from LDR/SDR content by us, it'll have a key indicating the Nit scale that was appplied.
|
||||
// We can use that to make viewing the file work out of the box.
|
||||
var ldrHDRUpconversionNitMultiplier = ktx2File.getLDRHDRUpconversionNitMultiplier();
|
||||
@@ -716,16 +776,17 @@
|
||||
|
||||
var total_texels = dumpKTX2FileDesc(ktx2File);
|
||||
var bpp = (data.byteLength * 8.0) / total_texels;
|
||||
descString += ` (${bpp.toFixed(3)} bits/pixel).`;
|
||||
var fileSizeKB = data.byteLength / 1024.0;
|
||||
descString += ` (${bpp.toFixed(3)} bits/pixel, ${fileSizeKB.toFixed(2)} KB).`;
|
||||
|
||||
elem('format').innerText = descString;
|
||||
|
||||
const dstSize = ktx2File.getImageTranscodedSizeInBytes(0, 0, 0, format);
|
||||
const dstSize = ktx2File.getImageTranscodedSizeInBytes(selectedLevel, selectedLayer, selectedFace, format);
|
||||
const dst = new Uint8Array(dstSize);
|
||||
|
||||
const levelIndex =0;
|
||||
const layerIndex = 0;
|
||||
const faceIndex = 0;
|
||||
const levelIndex = selectedLevel;
|
||||
const layerIndex = selectedLayer;
|
||||
const faceIndex = selectedFace;
|
||||
|
||||
var flags = elem('highquality_transcoding').checked ? Module.basisu_decode_flags.cDecodeFlagsHighQuality.value : 0;
|
||||
|
||||
@@ -765,7 +826,9 @@
|
||||
|
||||
g_transcodingTime = elapsed;
|
||||
|
||||
descString += '\nTexture Size: ' + width + 'x' + height + ', Levels: ' + levels + ', Layers: ' + layers + ', Faces: ' + faces + ', sRGB: ' + tex_is_srgb + ', alpha: ' + tex_has_alpha;
|
||||
descString += '\nTexture Size: ' + baseWidth + 'x' + baseHeight + ', Levels: ' + levels + ', Layers: ' + layers + ', Faces: ' + faces + ', sRGB: ' + tex_is_srgb + ', alpha: ' + tex_has_alpha;
|
||||
|
||||
descString += '\nViewing: Mip ' + selectedLevel + ' (' + width + 'x' + height + ') Face ' + selectedFace + ' Layer ' + selectedLayer;
|
||||
|
||||
descString += '\nTranscode time: ' + g_transcodingTime.toFixed(3) + 'ms';
|
||||
|
||||
@@ -800,6 +863,8 @@
|
||||
|
||||
log('ldrHDRUpconversionNitMultiplier: ' + ldrHDRUpconversionNitMultiplier);
|
||||
|
||||
log('selectedLevel: ' + selectedLevel + ' (' + width + 'x' + height + '), face: ' + selectedFace + ', layer: ' + selectedLayer);
|
||||
|
||||
logTime('transcoding time', elapsed.toFixed(3));
|
||||
|
||||
alignedWidth = Math.floor((width + dstBlockWidth - 1) / dstBlockWidth) * dstBlockWidth;
|
||||
@@ -1332,6 +1397,12 @@
|
||||
basisEncoder.setRDOUASTCQualityScalar(getUASTCLDRRDOQuality());
|
||||
|
||||
basisEncoder.setMipGen(elem('Mipmaps').checked);
|
||||
basisEncoder.setMipFilter(parseInt(elem('mip-filter-select').value, 10));
|
||||
basisEncoder.setMipScale(parseFloat(elem('mip-scale-input').value) || 1.0);
|
||||
basisEncoder.setMipSmallestDimension(parseInt(elem('mip-smallest-dim-input').value, 10) || 1);
|
||||
basisEncoder.setMipRenormalize(elem('MipRenormalize').checked);
|
||||
basisEncoder.setMipWrapping(elem('MipWrapping').checked);
|
||||
|
||||
basisEncoder.setYFlip(elem('YFlip').checked);
|
||||
|
||||
basisEncoder.setETC1SCompressionLevel(getETC1SCompLevel());
|
||||
@@ -1423,6 +1494,11 @@
|
||||
function runLoadFile()
|
||||
{
|
||||
//logClear();
|
||||
resetMipmapSliderToZero();
|
||||
resetCubemapFaceToZero();
|
||||
resetArrayLayerToZero();
|
||||
resetDisplayZoom();
|
||||
resetExposure();
|
||||
loadArrayBufferFromURI(elem('file').value, transcodeTexture, dataLoadError);
|
||||
}
|
||||
|
||||
@@ -1430,6 +1506,10 @@
|
||||
{
|
||||
//logClear();
|
||||
|
||||
resetMipmapSliderToZero();
|
||||
resetCubemapFaceToZero();
|
||||
resetArrayLayerToZero();
|
||||
|
||||
if ((elem('imagefile').value === '<externally loaded>') && (curLoadedImageData != null))
|
||||
{
|
||||
//console.log("calling compressImage 1");
|
||||
@@ -1452,6 +1532,12 @@
|
||||
|
||||
if (selectedFilename)
|
||||
{
|
||||
resetMipmapSliderToZero();
|
||||
resetCubemapFaceToZero();
|
||||
resetArrayLayerToZero();
|
||||
resetDisplayZoom();
|
||||
resetExposure();
|
||||
|
||||
elem('imagefile').value = selectedFilename;
|
||||
|
||||
//logClear();
|
||||
@@ -1525,6 +1611,185 @@
|
||||
redraw();
|
||||
}
|
||||
|
||||
function updateMipmapLevel(value)
|
||||
{
|
||||
var v = parseInt(value, 10);
|
||||
document.getElementById('mipmap-level-value').textContent = v;
|
||||
|
||||
// Re-transcode the current texture at the selected mip level.
|
||||
if (curLoadedKTX2Data != null)
|
||||
{
|
||||
transcodeTexture(curLoadedKTX2Data, curLoadedKTX2URI);
|
||||
}
|
||||
}
|
||||
|
||||
function updateDisplayZoom(value)
|
||||
{
|
||||
// Slider steps: 0=0.5x, 1=1x, 2=2x, ... 8=8x
|
||||
var v = parseInt(value, 10);
|
||||
displayZoom = (v === 0) ? 0.5 : v;
|
||||
document.getElementById('display-zoom-value').textContent = displayZoom + 'x';
|
||||
redraw();
|
||||
}
|
||||
|
||||
function toggleBilinearFiltering()
|
||||
{
|
||||
drawUseBilinearFiltering = document.getElementById('bilinear-filtering').checked;
|
||||
redraw();
|
||||
}
|
||||
|
||||
function resetDisplayZoom()
|
||||
{
|
||||
displayZoom = 1.0;
|
||||
document.getElementById('display-zoom-slider').value = 1;
|
||||
document.getElementById('display-zoom-value').textContent = '1x';
|
||||
}
|
||||
|
||||
function resetExposure()
|
||||
{
|
||||
drawScale = 1.0;
|
||||
document.getElementById('scale-slider').value = 0.5;
|
||||
document.getElementById('scale-value').textContent = '1.0000';
|
||||
}
|
||||
|
||||
// Updates the slider range to match the current texture's mip count.
|
||||
// Preserves the current slider value if it's still in range; otherwise clamps to 0.
|
||||
function updateMipmapSliderRange(numLevels)
|
||||
{
|
||||
var slider = document.getElementById('mipmap-level-slider');
|
||||
var label = document.getElementById('mipmap-level-value');
|
||||
|
||||
var maxLevel = Math.max(0, numLevels - 1);
|
||||
|
||||
slider.min = 0;
|
||||
slider.max = maxLevel;
|
||||
slider.disabled = (maxLevel === 0);
|
||||
|
||||
// Clamp current value to the new valid range.
|
||||
var curVal = parseInt(slider.value, 10);
|
||||
if (isNaN(curVal) || curVal < 0 || curVal > maxLevel)
|
||||
{
|
||||
slider.value = 0;
|
||||
label.textContent = '0';
|
||||
}
|
||||
}
|
||||
|
||||
// Resets the slider to level 0 (for when a new file is loaded).
|
||||
function resetMipmapSliderToZero()
|
||||
{
|
||||
var slider = document.getElementById('mipmap-level-slider');
|
||||
var label = document.getElementById('mipmap-level-value');
|
||||
|
||||
slider.value = 0;
|
||||
label.textContent = '0';
|
||||
}
|
||||
|
||||
function updateCubemapFace(value)
|
||||
{
|
||||
var v = parseInt(value, 10);
|
||||
document.getElementById('cubemap-face-value').textContent = v;
|
||||
|
||||
if (curLoadedKTX2Data != null)
|
||||
{
|
||||
transcodeTexture(curLoadedKTX2Data, curLoadedKTX2URI);
|
||||
}
|
||||
}
|
||||
|
||||
function updateCubemapFaceRange(numFaces)
|
||||
{
|
||||
var slider = document.getElementById('cubemap-face-slider');
|
||||
var label = document.getElementById('cubemap-face-value');
|
||||
|
||||
var maxFace = Math.max(0, numFaces - 1);
|
||||
|
||||
slider.min = 0;
|
||||
slider.max = maxFace;
|
||||
slider.disabled = (maxFace === 0);
|
||||
|
||||
var curVal = parseInt(slider.value, 10);
|
||||
if (isNaN(curVal) || curVal < 0 || curVal > maxFace)
|
||||
{
|
||||
slider.value = 0;
|
||||
label.textContent = '0';
|
||||
}
|
||||
}
|
||||
|
||||
function resetCubemapFaceToZero()
|
||||
{
|
||||
var slider = document.getElementById('cubemap-face-slider');
|
||||
var label = document.getElementById('cubemap-face-value');
|
||||
|
||||
slider.value = 0;
|
||||
label.textContent = '0';
|
||||
}
|
||||
|
||||
function updateArrayLayer(value)
|
||||
{
|
||||
var v = parseInt(value, 10);
|
||||
document.getElementById('array-layer-value').textContent = v;
|
||||
document.getElementById('array-layer-input').value = v;
|
||||
|
||||
if (curLoadedKTX2Data != null)
|
||||
{
|
||||
transcodeTexture(curLoadedKTX2Data, curLoadedKTX2URI);
|
||||
}
|
||||
}
|
||||
|
||||
function updateArrayLayerFromInput(value)
|
||||
{
|
||||
var v = parseInt(value, 10);
|
||||
var slider = document.getElementById('array-layer-slider');
|
||||
var maxLayer = parseInt(slider.max, 10);
|
||||
|
||||
if (isNaN(v) || v < 0) v = 0;
|
||||
if (v > maxLayer) v = maxLayer;
|
||||
|
||||
slider.value = v;
|
||||
document.getElementById('array-layer-value').textContent = v;
|
||||
document.getElementById('array-layer-input').value = v;
|
||||
|
||||
if (curLoadedKTX2Data != null)
|
||||
{
|
||||
transcodeTexture(curLoadedKTX2Data, curLoadedKTX2URI);
|
||||
}
|
||||
}
|
||||
|
||||
function updateArrayLayerRange(numLayers)
|
||||
{
|
||||
var slider = document.getElementById('array-layer-slider');
|
||||
var label = document.getElementById('array-layer-value');
|
||||
var input = document.getElementById('array-layer-input');
|
||||
|
||||
var maxLayer = Math.max(0, numLayers - 1);
|
||||
|
||||
slider.min = 0;
|
||||
slider.max = maxLayer;
|
||||
slider.disabled = (maxLayer === 0);
|
||||
|
||||
input.min = 0;
|
||||
input.max = maxLayer;
|
||||
input.disabled = (maxLayer === 0);
|
||||
|
||||
var curVal = parseInt(slider.value, 10);
|
||||
if (isNaN(curVal) || curVal < 0 || curVal > maxLayer)
|
||||
{
|
||||
slider.value = 0;
|
||||
input.value = 0;
|
||||
label.textContent = '0';
|
||||
}
|
||||
}
|
||||
|
||||
function resetArrayLayerToZero()
|
||||
{
|
||||
var slider = document.getElementById('array-layer-slider');
|
||||
var label = document.getElementById('array-layer-value');
|
||||
var input = document.getElementById('array-layer-input');
|
||||
|
||||
slider.value = 0;
|
||||
input.value = 0;
|
||||
label.textContent = '0';
|
||||
}
|
||||
|
||||
|
||||
|
||||
function downloadEncodedFile()
|
||||
@@ -2052,10 +2317,11 @@
|
||||
/* NEW: make the flex row fill the viewport and respect padding */
|
||||
height: 100vh;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Fixed-width controls column (never shrinks) */
|
||||
.controls { flex: 0 0 600px; width: 600px; }
|
||||
/* Fixed-width controls column (never shrinks), scrolls independently */
|
||||
.controls { flex: 0 0 600px; width: 600px; overflow-y: auto; max-height: calc(100vh - 24px); }
|
||||
|
||||
/* Viewer takes the remaining space and scrolls when texture is huge */
|
||||
/* EDIT: cap height so vertical scrolling appears inside the viewer */
|
||||
@@ -2071,8 +2337,8 @@
|
||||
|
||||
/* Small screens: stack vertically so it’s still usable */
|
||||
@media (max-width: 1100px){
|
||||
.wrap{ flex-direction:column; height:auto; }
|
||||
.controls{ flex: 0 0 auto; width:auto; }
|
||||
.wrap{ flex-direction:column; height:auto; overflow:auto; }
|
||||
.controls{ flex: 0 0 auto; width:auto; max-height:none; overflow-y:visible; }
|
||||
.viewer{ flex: 1 1 auto; max-height:none; }
|
||||
}
|
||||
|
||||
@@ -2105,7 +2371,7 @@
|
||||
|
||||
<br>
|
||||
<div style="font-size: 24pt; font-weight: bold">
|
||||
Basis Universal .KTX2 Supercompressed GPU Texture Encoding/Transcoding Testbed v2.10
|
||||
Basis Universal .KTX2 Supercompressed GPU Texture Encoding/Transcoding Testbed v2.11
|
||||
</div>
|
||||
|
||||
<br>This simple demo uses the <a href="https://github.com/BinomialLLC/basis_universal/">Basis Universal</a> C++ transcoder (compiled to WebAssembly using Emscripten) to transcode a .ktx2 file to:
|
||||
@@ -2289,8 +2555,60 @@
|
||||
|
||||
<br>
|
||||
<hr style="width: 40%; margin: 10px 0; background-color: #ccc; border: none; height: 1px;">
|
||||
|
||||
<b>Display/Visualization Options:</b>
|
||||
<br>
|
||||
|
||||
<b>Transcoder Options (Decode Flags):</b>
|
||||
<input type="button" value="Alpha blend" onclick="alphaBlend()"></input>
|
||||
<input type="button" value="View RGB" onclick="viewRGB()"></input>
|
||||
<input type="button" value="View Alpha" onclick="viewAlpha()"></input>
|
||||
|
||||
<br>
|
||||
|
||||
<br>
|
||||
<input type="button" value="LinearToSRGB" onclick="linearToSRGB()"></input> <b id='linear_to_srgb'>Disabled</b>
|
||||
|
||||
<br>
|
||||
|
||||
<label for="scale-slider">Exposure:</label>
|
||||
<input type="range" id="scale-slider" min="0" max="1" step=".01" value=".5" style="width: 300px;" oninput="updateScale(this.value)">
|
||||
<span id="scale-value">1.0000</span>
|
||||
|
||||
<br>
|
||||
|
||||
<label for="mipmap-level-slider">Mipmap Level:</label>
|
||||
<input type="range" id="mipmap-level-slider" min="0" max="0" step="1" value="0" style="width: 300px;" oninput="updateMipmapLevel(this.value)" disabled>
|
||||
<span id="mipmap-level-value">0</span>
|
||||
|
||||
<br>
|
||||
|
||||
<label for="cubemap-face-slider">Cubemap Face:</label>
|
||||
<input type="range" id="cubemap-face-slider" min="0" max="0" step="1" value="0" style="width: 300px;" oninput="updateCubemapFace(this.value)" disabled>
|
||||
<span id="cubemap-face-value">0</span>
|
||||
|
||||
<br>
|
||||
|
||||
<label for="array-layer-slider">Array Layer:</label>
|
||||
<input type="range" id="array-layer-slider" min="0" max="0" step="1" value="0" style="width: 300px;" oninput="updateArrayLayer(this.value)" disabled>
|
||||
<span id="array-layer-value">0</span>
|
||||
<input type="number" id="array-layer-input" min="0" max="0" value="0" style="width: 60px; margin-left: 8px;" onchange="updateArrayLayerFromInput(this.value)" disabled>
|
||||
|
||||
<br>
|
||||
|
||||
<label for="display-zoom-slider">Display Zoom:</label>
|
||||
<input type="range" id="display-zoom-slider" min="0" max="8" step="1" value="1" style="width: 300px;" oninput="updateDisplayZoom(this.value)">
|
||||
<span id="display-zoom-value">1x</span>
|
||||
|
||||
<br>
|
||||
|
||||
Bilinear Filtering:
|
||||
<input type="checkbox" id="bilinear-filtering" onclick="toggleBilinearFiltering()">
|
||||
|
||||
<br>
|
||||
|
||||
<hr style="width: 40%; margin: 10px 0; background-color: #ccc; border: none; height: 1px;">
|
||||
|
||||
<b>Transcoder Options (Decode Flags):</b>
|
||||
|
||||
<br>
|
||||
ETC1S: No BC7 Chroma Artifact Filtering (faster transcoding):
|
||||
@@ -2318,29 +2636,8 @@
|
||||
<input type="checkbox" id="highquality_transcoding" onclick="highQualityTranscodingClicked()">
|
||||
|
||||
<br>
|
||||
|
||||
<hr style="width: 40%; margin: 10px 0; background-color: #ccc; border: none; height: 1px;">
|
||||
|
||||
<b>Display/Visualization Options:</b>
|
||||
<br>
|
||||
|
||||
<input type="button" value="Alpha blend" onclick="alphaBlend()"></input>
|
||||
<input type="button" value="View RGB" onclick="viewRGB()"></input>
|
||||
<input type="button" value="View Alpha" onclick="viewAlpha()"></input>
|
||||
|
||||
<br>
|
||||
|
||||
<br>
|
||||
<input type="button" value="LinearToSRGB" onclick="linearToSRGB()"></input> <b id='linear_to_srgb'>Disabled</b>
|
||||
|
||||
<br>
|
||||
|
||||
<label for="scale-slider">Exposure:</label>
|
||||
<input type="range" id="scale-slider" min="0" max="1" step=".01" value="1" style="width: 300px;" oninput="updateScale(this.value)">
|
||||
<span id="scale-value">1</span>
|
||||
|
||||
<br>
|
||||
|
||||
<hr style="width: 40%; margin: 10px 0; background-color: #ccc; border: none; height: 1px;">
|
||||
|
||||
<b>Low-level ETC1S LDR Options:</b>
|
||||
<br>
|
||||
@@ -2477,10 +2774,6 @@
|
||||
Image is sRGB/use sRGB perceptual metrics:
|
||||
<input type="checkbox" id="SRGB">
|
||||
|
||||
<br>
|
||||
Generate mipmap levels:
|
||||
<input type="checkbox" id="Mipmaps">
|
||||
|
||||
<br>
|
||||
Y flip source image:
|
||||
<input type="checkbox" id="YFlip">
|
||||
@@ -2494,6 +2787,54 @@
|
||||
|
||||
<hr style="width: 40%; margin: 10px 0; background-color: #ccc; border: none; height: 1px;">
|
||||
|
||||
<b>Mipmap Generation Options:</b>
|
||||
<br>
|
||||
|
||||
Generate mipmap levels:
|
||||
<input type="checkbox" id="Mipmaps">
|
||||
|
||||
<br>
|
||||
|
||||
<label for="mip-filter-select">Mip Filter:</label>
|
||||
<select id="mip-filter-select">
|
||||
<option value="0">box</option>
|
||||
<option value="1">tent</option>
|
||||
<option value="2">bell</option>
|
||||
<option value="3">b-spline</option>
|
||||
<option value="4">mitchell</option>
|
||||
<option value="5">blackman</option>
|
||||
<option value="6">lanczos3</option>
|
||||
<option value="7">lanczos4</option>
|
||||
<option value="8">lanczos6</option>
|
||||
<option value="9">lanczos12</option>
|
||||
<option value="10" selected>kaiser</option>
|
||||
<option value="11">gaussian</option>
|
||||
<option value="12">catmullrom</option>
|
||||
<option value="13">quadratic_interp</option>
|
||||
<option value="14">quadratic_approx</option>
|
||||
<option value="15">quadratic_mix</option>
|
||||
</select>
|
||||
|
||||
<br>
|
||||
|
||||
<label for="mip-scale-input">Mip Scale:</label>
|
||||
<input type="number" id="mip-scale-input" value="1.0" min="0.000125" max="4.0" step="0.1" style="width: 80px;">
|
||||
|
||||
<br>
|
||||
|
||||
<label for="mip-smallest-dim-input">Smallest Mip Dimension:</label>
|
||||
<input type="number" id="mip-smallest-dim-input" value="1" min="1" max="16384" step="1" style="width: 80px;">
|
||||
|
||||
<br>
|
||||
|
||||
Mip Renormalize:
|
||||
<input type="checkbox" id="MipRenormalize">
|
||||
|
||||
Mip Wrapping:
|
||||
<input type="checkbox" id="MipWrapping" checked>
|
||||
|
||||
<hr style="width: 40%; margin: 10px 0; background-color: #ccc; border: none; height: 1px;">
|
||||
|
||||
<b>Log Output:</b>
|
||||
<div id="log-panel" class="log-panel">
|
||||
</div>
|
||||
@@ -2667,6 +3008,12 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
if (!file)
|
||||
return;
|
||||
|
||||
resetMipmapSliderToZero();
|
||||
resetCubemapFaceToZero();
|
||||
resetArrayLayerToZero();
|
||||
resetDisplayZoom();
|
||||
resetExposure();
|
||||
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.onload = function (e)
|
||||
|
||||
Reference in New Issue
Block a user