Add new texture images: stripes.png, stripes_02.png, woodgrain_02.jpg, and woodgrain_03.jpg
- Added stripes.png and stripes_02.png with metadata from Adobe Photoshop. - Included two new woodgrain textures (woodgrain_02.jpg and woodgrain_03.jpg) for enhanced visual variety.
@@ -153,6 +153,15 @@ const _LOG_MAX = Math.log(10);
|
|||||||
const scaleToPos = v => Math.round((Math.log(Math.max(0.05, Math.min(10, v))) - _LOG_MIN) / (_LOG_MAX - _LOG_MIN) * 1000);
|
const scaleToPos = v => Math.round((Math.log(Math.max(0.05, Math.min(10, v))) - _LOG_MIN) / (_LOG_MAX - _LOG_MIN) * 1000);
|
||||||
const posToScale = p => parseFloat(Math.exp(_LOG_MIN + (p / 1000) * (_LOG_MAX - _LOG_MIN)).toFixed(2));
|
const posToScale = p => parseFloat(Math.exp(_LOG_MIN + (p / 1000) * (_LOG_MAX - _LOG_MIN)).toFixed(2));
|
||||||
|
|
||||||
|
function _applyScaleU(v) {
|
||||||
|
v = Math.max(0.05, Math.min(10, v));
|
||||||
|
settings.scaleU = v;
|
||||||
|
scaleUSlider.value = scaleToPos(v);
|
||||||
|
scaleUVal.value = v;
|
||||||
|
if (settings.lockScale) { settings.scaleV = v; scaleVSlider.value = scaleToPos(v); scaleVVal.value = v; }
|
||||||
|
clearTimeout(previewDebounce); previewDebounce = setTimeout(updatePreview, 80);
|
||||||
|
}
|
||||||
|
|
||||||
// ── Init ──────────────────────────────────────────────────────────────────────
|
// ── Init ──────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
let PRESETS = [];
|
let PRESETS = [];
|
||||||
@@ -230,6 +239,7 @@ function selectPreset(idx, swatchEl) {
|
|||||||
activeMapEntry = PRESETS[idx];
|
activeMapEntry = PRESETS[idx];
|
||||||
activeMapName.textContent = PRESETS[idx].name;
|
activeMapName.textContent = PRESETS[idx].name;
|
||||||
resetTextureSmoothing();
|
resetTextureSmoothing();
|
||||||
|
if (activeMapEntry.defaultScale != null) _applyScaleU(activeMapEntry.defaultScale);
|
||||||
updatePreview();
|
updatePreview();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -299,14 +309,7 @@ function wireEvents() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Scale U — when lock is on, mirror to V
|
// Scale U — when lock is on, mirror to V
|
||||||
const applyScaleU = (v) => {
|
const applyScaleU = (v) => _applyScaleU(v);
|
||||||
v = Math.max(0.05, Math.min(10, v));
|
|
||||||
settings.scaleU = v;
|
|
||||||
scaleUSlider.value = scaleToPos(v);
|
|
||||||
scaleUVal.value = v;
|
|
||||||
if (settings.lockScale) { settings.scaleV = v; scaleVSlider.value = scaleToPos(v); scaleVVal.value = v; }
|
|
||||||
clearTimeout(previewDebounce); previewDebounce = setTimeout(updatePreview, 80);
|
|
||||||
};
|
|
||||||
scaleUSlider.addEventListener('input', () => applyScaleU(posToScale(parseFloat(scaleUSlider.value))));
|
scaleUSlider.addEventListener('input', () => applyScaleU(posToScale(parseFloat(scaleUSlider.value))));
|
||||||
scaleUSlider.addEventListener('dblclick', () => applyScaleU(posToScale(parseFloat(scaleUSlider.defaultValue))));
|
scaleUSlider.addEventListener('dblclick', () => applyScaleU(posToScale(parseFloat(scaleUSlider.defaultValue))));
|
||||||
scaleUVal.addEventListener('change', () => applyScaleU(parseFloat(scaleUVal.value)));
|
scaleUVal.addEventListener('change', () => applyScaleU(parseFloat(scaleUVal.value)));
|
||||||
|
|||||||
@@ -29,27 +29,34 @@ function fitDimensions(imgW, imgH) {
|
|||||||
// ── Image-based presets ───────────────────────────────────────────────────────
|
// ── Image-based presets ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
const IMAGE_PRESETS = [
|
const IMAGE_PRESETS = [
|
||||||
{ name: 'Basket', url: 'textures/basket.jpg' },
|
{ name: 'Basket', url: 'textures/basket.jpg', defaultScale: 0.5 },
|
||||||
{ name: 'Brick', url: 'textures/brick.jpg' },
|
{ name: 'Brick', url: 'textures/brick.jpg', defaultScale: 0.5 },
|
||||||
{ name: 'Bubble', url: 'textures/bubble.jpg' },
|
{ name: 'Bubble', url: 'textures/bubble.jpg', defaultScale: 0.5 },
|
||||||
{ name: 'Carbon Fiber', url: 'textures/carbonFiber.jpg' },
|
{ name: 'Carbon Fiber', url: 'textures/carbonFiber.jpg', defaultScale: 0.5 },
|
||||||
{ name: 'Crystal', url: 'textures/crystal.jpg' },
|
{ name: 'Crystal', url: 'textures/crystal.jpg', defaultScale: 0.5 },
|
||||||
{ name: 'Dots', url: 'textures/dots.jpg' },
|
{ name: 'Dots', url: 'textures/dots.jpg', defaultScale: 0.1 },
|
||||||
{ name: 'Grip Surface', url: 'textures/gripSurface.jpg' },
|
{ name: 'Grip Surface', url: 'textures/gripSurface.jpg', defaultScale: 0.5 },
|
||||||
{ name: 'Hexagons', url: 'textures/hexagons.jpg' },
|
{ name: 'Hexagon', url: 'textures/hexagon.jpg', defaultScale: 0.5 },
|
||||||
{ name: 'Knitting', url: 'textures/knitting.jpg' },
|
{ name: 'Hexagons', url: 'textures/hexagons.jpg', defaultScale: 1.0 },
|
||||||
{ name: 'Knurling', url: 'textures/knurling.jpg' },
|
{ name: 'Isogrid', url: 'textures/isogrid.png', defaultScale: 0.5 },
|
||||||
{ name: 'Leather', url: 'textures/leather.jpg' },
|
{ name: 'Knitting', url: 'textures/knitting.jpg', defaultScale: 0.25 },
|
||||||
{ name: 'Leather 2', url: 'textures/leather2.jpg' },
|
{ name: 'Knurling', url: 'textures/knurling.jpg', defaultScale: 0.15 },
|
||||||
{ name: 'Noise', url: 'textures/noise.jpg' },
|
{ name: 'Leather', url: 'textures/leather.jpg', defaultScale: 0.5 },
|
||||||
{ name: 'Voronoi', url: 'textures/voronoi.jpg' },
|
{ name: 'Leather 2', url: 'textures/leather2.jpg', defaultScale: 0.5 },
|
||||||
{ name: 'Weave', url: 'textures/weave.jpg' },
|
{ name: 'Noise', url: 'textures/noise.jpg', defaultScale: 0.3 },
|
||||||
{ name: 'Weave 02', url: 'textures/weave_02.jpg' },
|
{ name: 'Stripes', url: 'textures/stripes.png', defaultScale: 0.5 },
|
||||||
{ name: 'Weave 03', url: 'textures/weave_03.jpg' },
|
{ name: 'Stripes 02', url: 'textures/stripes_02.png', defaultScale: 1.0 },
|
||||||
{ name: 'Wood', url: 'textures/wood.jpg' },
|
{ name: 'Voronoi', url: 'textures/voronoi.jpg', defaultScale: 0.5 },
|
||||||
|
{ name: 'Weave', url: 'textures/weave.jpg', defaultScale: 0.5 },
|
||||||
|
{ name: 'Weave 02', url: 'textures/weave_02.jpg', defaultScale: 0.5 },
|
||||||
|
{ name: 'Weave 03', url: 'textures/weave_03.jpg', defaultScale: 0.5 },
|
||||||
|
{ name: 'Wood', url: 'textures/wood.jpg', defaultScale: 0.5 },
|
||||||
|
{ name: 'Wood Grain 02',url: 'textures/woodgrain_02.jpg', defaultScale: 1.0 },
|
||||||
|
{ name: 'Wood Grain 03',url: 'textures/woodgrain_03.jpg', defaultScale: 1.0 },
|
||||||
];
|
];
|
||||||
|
|
||||||
function loadImagePreset({ name, url }) {
|
function loadImagePreset(preset) {
|
||||||
|
const { name, url } = preset;
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const img = new Image();
|
const img = new Image();
|
||||||
img.onload = () => {
|
img.onload = () => {
|
||||||
@@ -65,7 +72,7 @@ function loadImagePreset({ name, url }) {
|
|||||||
texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
|
texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
|
||||||
texture.name = name;
|
texture.name = name;
|
||||||
|
|
||||||
resolve({ name, thumbCanvas: thumb, fullCanvas: full, texture, imageData, width: w, height: h });
|
resolve({ name, thumbCanvas: thumb, fullCanvas: full, texture, imageData, width: w, height: h, defaultScale: preset.defaultScale });
|
||||||
};
|
};
|
||||||
img.onerror = () => reject(new Error(`Failed to load preset image: ${url}`));
|
img.onerror = () => reject(new Error(`Failed to load preset image: ${url}`));
|
||||||
img.src = url;
|
img.src = url;
|
||||||
|
|||||||
|
After Width: | Height: | Size: 80 KiB |
|
After Width: | Height: | Size: 9.1 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 1.1 MiB |