-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathindex.html
More file actions
725 lines (660 loc) · 44.5 KB
/
index.html
File metadata and controls
725 lines (660 loc) · 44.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
<!DOCTYPE html>
<html lang="en" prefix="og: https://ogp.me/ns#">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mesh Texture | 3D Texture Mapping & STL Embossing Tool</title>
<!-- SEO Meta Tags -->
<meta name="description"
content="STL dosyalarınıza profesyonel 3D dokular ekleyin. 3D baskı ve mühendislik projeleri için optimize edilmiş, tarayıcı tabanlı ücretsiz doku kaplama aracı.">
<meta name="keywords" content="
3D texture mapping, STL texture, 3D printing tool, mesh embossing, meshtexture, 3D model texturing,
3D Textur Mapping, STL Textur, 3D Druck Werkzeug, Oberflächenstruktur 3D,
cartographie de texture 3D, texture STL, outil impression 3D, embossage maillage,
mapeo de textura 3D, textura STL, herramienta impresión 3D, relieve malla 3D,
mappatura texture 3D, texture STL, strumento stampa 3D, rilievo mesh,
3D textuur mapping, STL textuur, 3D print tool, mesh reliëf,
mapowanie tekstur 3D, tekstura STL, narzędzie do druku 3D,
mapeamento de textura 3D, textura STL, ferramenta impressão 3D,
3D текстур маппинг, текстура STL, инструмент 3D печати,
3Dテクスチャマッピング, STLテクスチャ, 3Dプリントツール,
3D纹理贴图, STL纹理, 3D打印工具, 网格浮雕,
3D텍스처 매핑, STL 텍스처, 3D 프린팅 도구,
تعيين نسيج ثلاثي الأبعاد, نسيج STL, أداة طباعة ثلاثية الأبعاد,
STL doku kaplama, 3D doku ekleme, ücretsiz doku aracı, mesh kabartma
">
<meta name="robots" content="index, follow">
<meta name="author" content="Feridun Oktar">
<!-- Open Graph -->
<meta property="og:title" content="Mesh Texture | 3D Texture Mapping & STL Embossing Tool">
<meta property="og:description"
content="Apply professional 3D textures to your STL files. A free, browser-based texture mapping tool optimised for 3D printing and engineering projects.">
<meta property="og:type" content="website">
<meta property="og:url" content="https://www.meshtexture.com/">
<meta property="og:locale" content="en_US">
<meta property="og:locale:alternate" content="tr_TR">
<meta property="og:locale:alternate" content="de_DE">
<meta property="og:locale:alternate" content="fr_FR">
<meta property="og:locale:alternate" content="es_ES">
<meta property="og:locale:alternate" content="it_IT">
<meta property="og:locale:alternate" content="pt_PT">
<meta property="og:locale:alternate" content="ru_RU">
<meta property="og:locale:alternate" content="ja_JP">
<meta property="og:locale:alternate" content="zh_CN">
<meta property="og:locale:alternate" content="ko_KR">
<meta property="og:locale:alternate" content="ar_SA">
<meta property="og:locale:alternate" content="nl_NL">
<meta property="og:locale:alternate" content="pl_PL">
<!-- Canonical & Hreflang (16 locales) -->
<link rel="canonical" href="https://www.meshtexture.com/">
<!-- Active translations -->
<link rel="alternate" hreflang="en" href="https://www.meshtexture.com/?lang=en">
<link rel="alternate" hreflang="tr" href="https://www.meshtexture.com/?lang=tr">
<!-- International — all point to the canonical URL until locale paths are live -->
<link rel="alternate" hreflang="de" href="https://www.meshtexture.com/">
<link rel="alternate" hreflang="fr" href="https://www.meshtexture.com/">
<link rel="alternate" hreflang="es" href="https://www.meshtexture.com/">
<link rel="alternate" hreflang="it" href="https://www.meshtexture.com/">
<link rel="alternate" hreflang="nl" href="https://www.meshtexture.com/">
<link rel="alternate" hreflang="pl" href="https://www.meshtexture.com/">
<link rel="alternate" hreflang="pt" href="https://www.meshtexture.com/">
<link rel="alternate" hreflang="ru" href="https://www.meshtexture.com/">
<link rel="alternate" hreflang="ja" href="https://www.meshtexture.com/">
<link rel="alternate" hreflang="zh" href="https://www.meshtexture.com/">
<link rel="alternate" hreflang="ko" href="https://www.meshtexture.com/">
<link rel="alternate" hreflang="ar" href="https://www.meshtexture.com/">
<link rel="alternate" hreflang="x-default" href="https://www.meshtexture.com/">
<!-- JSON-LD Structured Data -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "SoftwareApplication",
"name": "Mesh Texture Pro",
"url": "https://www.meshtexture.com/",
"applicationCategory": "DesignApplication",
"operatingSystem": "Web",
"description": "A high-performance web tool for applying procedural 3D textures to STL meshes. Supports triplanar and spherical mapping, real-time preview, and direct STL export.",
"offers": {
"@type": "Offer",
"price": "0",
"priceCurrency": "USD"
},
"author": {
"@type": "Person",
"name": "Feridun Oktar"
},
"inLanguage": ["en", "tr", "de", "fr", "es", "it", "nl", "pl", "pt", "ru", "ja", "zh", "ko", "ar"]
}
</script>
<link rel="stylesheet" href="src/style.css">
<link rel="stylesheet" href="src/input_styles.css">
<!-- Favicon (multi-size) -->
<link rel="icon" type="image/png" sizes="48x48" href="assets/images/logov1.png">
<link rel="icon" type="image/png" sizes="32x32" href="assets/images/logov1.png">
<link rel="icon" type="image/png" sizes="16x16" href="assets/images/logov1.png">
<link rel="shortcut icon" href="assets/images/logov1.png">
<!-- Apple Touch Icon (iOS home screen / Safari tab) -->
<link rel="apple-touch-icon" sizes="180x180" href="assets/images/logov1.png">
<!-- Social sharing image (OG) -->
<meta property="og:image" content="https://www.meshtexture.com/assets/images/logov1.png">
<meta property="og:image:alt" content="Mesh Texture Pro logo">
<meta name="twitter:image" content="https://www.meshtexture.com/assets/images/logov1.png">
<!-- Import Map for Three.js -->
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/three@0.160.0/build/three.module.js",
"three/addons/": "https://unpkg.com/three@0.160.0/examples/jsm/"
}
}
</script>
<script>
// Only load Vercel Analytics on production (not localhost / local dev)
if (location.hostname !== 'localhost' && location.hostname !== '127.0.0.1') {
const s = document.createElement('script');
s.defer = true;
s.src = '/_vercel/insights/script.js';
document.head.appendChild(s);
}
</script>
</head>
<body>
<!-- Language Toggle Button (Floating) -->
<div id="langToggle" class="lang-toggle">
<button class="lang-btn active" data-lang="en">EN</button>
<button class="lang-btn" data-lang="tr">TR</button>
</div>
<!-- Sidebar Toggle Button (Hamburger) -->
<button id="sidebarToggle" class="sidebar-toggle">
<span></span>
<span></span>
<span></span>
</button>
<!-- Header Links Wrapper -->
<div id="bmcWrapper" class="bmc-wrapper">
<a href="https://discord.gg/kTH9TECaM" target="_blank" class="playstore-button" title="Join our Discord Community!">
<svg class="icon" viewBox="0 0 127.14 96.36">
<path d="M107.7,8.07A105.15,105.15,0,0,0,81.47,0a72.06,72.06,0,0,0-3.36,6.83A97.68,97.68,0,0,0,49,6.83,72.37,72.37,0,0,0,45.64,0,105.89,105.89,0,0,0,19.39,8.09C2.79,32.65-1.71,56.6.54,80.21h0A105.73,105.73,0,0,0,32.71,96.36,77.7,77.7,0,0,0,39.6,85.25a68.42,68.42,0,0,1-10.85-5.18c.91-.66,1.8-1.34,2.66-2a75.57,75.57,0,0,0,64.32,0c.87.71,1.76,1.39,2.66,2a68.68,68.68,0,0,1-10.87,5.19,77.5,77.5,0,0,0,6.89,11.1,105.25,105.25,0,0,0,32.19-16.14h0C129.24,52.84,122.09,29.11,107.7,8.07ZM42.45,65.69C36.18,65.69,31,60,31,53s5-12.74,11.43-12.74S54,46,53.89,53,48.84,65.69,42.45,65.69Zm42.24,0C78.41,65.69,73.31,60,73.31,53s5-12.74,11.43-12.74S96.2,46,96.12,53,91.08,65.69,84.69,65.69Z"/>
</svg>
</a>
<script type="text/javascript" src="https://cdnjs.buymeacoffee.com/1.0.0/button.prod.min.js" data-name="bmc-button" data-slug="feridun" data-color="#2196F3" data-emoji="☕" data-font="Lato" data-text="Buy me a coffee" data-outline-color="#000000" data-font-color="#ffffff" data-coffee-color="#FFDD00" ></script>
</div>
<!-- Theme Toggle Button -->
<!-- Theme Toggle (Checkbox based) -->
<label id="themeToggle" class="theme-toggle st-sunMoonThemeToggleBtn" title="Toggle Light/Dark Mode">
<input type="checkbox" id="themeCheckbox" class="themeToggleInput">
<svg viewBox="0 0 24 24" fill="currentColor" stroke="none" class="theme-toggle-svg">
<mask id="moon-mask">
<rect x="0" y="0" width="100%" height="100%" fill="white"></rect>
<circle cx="16" cy="12" r="6" fill="black"></circle>
</mask>
<circle class="sunMoon" cx="12" cy="12" r="6" mask="url(#moon-mask)"></circle>
<g class="sunRays" stroke="currentColor" stroke-width="2" stroke-linecap="round">
<line class="sunRay sunRay1" x1="12" y1="1" x2="12" y2="3"></line>
<line class="sunRay sunRay2" x1="12" y1="21" x2="12" y2="23"></line>
<line class="sunRay sunRay3" x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
<line class="sunRay sunRay4" x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
<line class="sunRay sunRay5" x1="1" y1="12" x2="3" y2="12"></line>
<line class="sunRay sunRay6" x1="21" y1="12" x2="23" y2="12"></line>
<line class="sunRay sunRay7" x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
<line class="sunRay sunRay8" x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
</g>
</svg>
</label>
<!-- GitHub Button (Floating) -->
<a href="https://github.com/feridun35/MeshTexture" target="_blank" id="githubBtn" class="theme-toggle"
title="View on GitHub">
<svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" width="20" height="20">
<path
d="M7.49933 0.25C3.49635 0.25 0.25 3.49593 0.25 7.50024C0.25 10.703 2.32715 13.4206 5.2081 14.3797C5.57084 14.446 5.70302 14.2222 5.70302 14.0299C5.70302 13.8576 5.69679 13.4019 5.69323 12.797C3.67661 13.235 3.25112 11.825 3.25112 11.825C2.92132 10.9874 2.44599 10.7644 2.44599 10.7644C1.78773 10.3149 2.49584 10.3238 2.49584 10.3238C3.22353 10.375 3.60629 11.0711 3.60629 11.0711C4.25298 12.1788 5.30335 11.8588 5.71638 11.6732C5.78225 11.205 5.96962 10.8854 6.17658 10.7043C4.56675 10.5209 2.87415 9.89918 2.87415 7.12104C2.87415 6.32925 3.15677 5.68257 3.62053 5.17563C3.54576 4.99226 3.29697 4.25521 3.69174 3.25691C3.69174 3.25691 4.30015 3.06196 5.68522 3.99973C6.26337 3.83906 6.8838 3.75895 7.50022 3.75583C8.1162 3.75895 8.73619 3.83906 9.31523 3.99973C10.6994 3.06196 11.3069 3.25691 11.3069 3.25691C11.7026 4.25521 11.4538 4.99226 11.3795 5.17563C11.8441 5.68257 12.1245 6.32925 12.1245 7.12104C12.1245 9.9063 10.4292 10.5192 8.81452 10.6985C9.07444 10.9224 9.30633 11.3648 9.30633 12.0413C9.30633 13.0102 9.29742 13.7922 9.29742 14.0299C9.29742 14.2239 9.42828 14.4496 9.79591 14.3788C12.6746 13.4179 14.75 10.7025 14.75 7.50024C14.75 3.49593 11.5036 0.25 7.49933 0.25Z"
fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path>
</svg>
</a>
<!-- Undo/Redo Group (Floating) -->
<div id="undoRedoGroup" class="undo-redo-floating">
<button id="undoBtn" class="header-icon-btn disabled" title="Undo (Ctrl+Z)" disabled>
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M9 14 4 9l5-5" />
<path d="M4 9h10.5a5.5 5.5 0 0 1 5.5 5.5v0a5.5 5.5 0 0 1-5.5 5.5H11" />
</svg>
</button>
<button id="redoBtn" class="header-icon-btn disabled" title="Redo (Ctrl+Y)" disabled>
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="m15 14 5-5-5-5" />
<path d="M20 9H9.5A5.5 5.5 0 0 0 4 14.5v0A5.5 5.5 0 0 0 9.5 20H13" />
</svg>
</button>
</div>
<!-- Sidebar -->
<aside id="sidebar" class="sidebar">
<!-- Header Removed -->
<div class="sidebar-content">
<!-- App Logo -->
<div class="sidebar-logo">
<div class="logo-main" data-i18n="appTitle">3D Mesh Texture</div>
<div class="logo-sub" data-i18n="appSubtitle">by Feridun Oktar</div>
</div>
<!-- File Operations -->
<div class="panel-section">
<div class="section-header-row">
<h2 data-i18n="fileOpsTitle">File Operations</h2>
<div class="header-controls">
<div id="helpIcon" class="help-icon" title="Show Guidance">?</div>
<div class="help-tooltip">
<h3 data-i18n="workflowGuideTitle">Workflow Guide</h3>
<div class="tooltip-steps">
<div><span class="t-num">1</span> <span data-i18n="guideStep1">Load .STL</span></div>
<div><span class="t-num">2</span> <span data-i18n="guideStep2">Select Faces</span></div>
<div><span class="t-num">3</span> <span data-i18n="guideStep4">Load Map</span> <span
class="fmt" data-i18n="guideStep4Sub">(JPG, PNG... HTML)</span>
</div>
<div><span class="t-num">4</span> <span data-i18n="guideStep5">Adjust Settings</span>
</div>
<div><span class="t-num">5</span> <span data-i18n="guideStep6">Apply & Export</span>
</div>
</div>
<h3 data-i18n="controlsTitle">Viewport Controls</h3>
<div class="tooltip-controls">
<div><strong><span data-i18n="rotateCtrl">Rotate</span></strong> <span
data-i18n="rotateKey">Left Click + Drag</span></div>
<div><strong><span data-i18n="panCtrl">Pan</span></strong> <span
data-i18n="panKey">Right Click + Drag</span></div>
<div><strong><span data-i18n="zoomCtrl">Zoom</span></strong> <span
data-i18n="zoomKey">Scroll Wheel</span></div>
</div>
</div>
</div>
</div>
<div class="button-group">
<label for="stlInput" class="load-stl-btn" title="Load .STL Model">
<span data-i18n="loadStl">Load STL</span>
<input type="file" id="stlInput" accept=".stl" hidden>
</label>
<label for="textureLoader" class="upload-texture-btn" id="uploadBtn" title="Upload Texture Map">
<span style="white-space: normal; line-height: 1; font-size: 0.75rem;"
data-i18n="uploadTexture">Upload<br>Texture</span>
<input type="file" id="textureLoader" accept="image/*" style="display: none;">
</label>
<button class="uiverse-btn secondary" id="selectMapBtn" title="Select Preset">
<span style="white-space: normal; line-height: 1; font-size: 0.75rem;"
data-i18n="selectTexture">Select<br>Texture</span>
</button>
</div>
<!-- Preset Popover (Initially Hidden) - REMOVED DUPLICATE -->
</div>
<!-- Selection Tools -->
<div class="panel-section">
<div class="section-header-row">
<h2 data-i18n="selectionTitle">Selection</h2>
<div class="galahhad-switch-wrapper">
<label class="switch">
<input type="checkbox" id="smartFillToggle" checked>
<span class="slider round"></span>
</label>
<span class="label-text" data-i18n="smartFill">Smart Fill</span>
</div>
</div>
<div class="control-row tooltip-container slider-row-horizontal">
<label data-i18n="angleThreshold">Angle Threshold</label>
<div class="range-slider-wrapper">
<div class="value-bubble" id="angleBubble">30</div>
<input type="range" id="angleThreshold" class="modern-range" min="1" max="90" value="30">
</div>
<!-- Hidden Span for old JS ref (optional, but cleaner to remove if JS updated) -->
<!-- <span id="angleValue">30°</span> -->
<div class="tooltip" data-i18n="angleTooltip">Determines surface continuity based on face normals.
</div>
</div>
<div class="control-row" style="margin-top: 10px; margin-bottom: 10px;">
<div id="paintModeContainer"
style="display: flex; margin-bottom: 10px; justify-content: space-between; align-items: center; position: relative;">
<span style="font-size: 0.85rem; color: var(--text-main);" data-i18n="paintArea">Paint
Area</span>
<label class="switch" style="transform: scale(0.85); transform-origin: right center;">
<input type="checkbox" id="paintModeToggle">
<span class="slider round"></span>
</label>
</div>
<!-- Lock Selection Toggle (Hidden by Default) -->
<div id="lockSelectionContainer"
style="display: flex; opacity: 0.5; pointer-events: none; margin-bottom: 10px; justify-content: space-between; align-items: center;">
<span style="font-size: 0.85rem; color: var(--text-main);" data-i18n="lockSelection">Lock
Selection</span>
<label class="lock-switch"
style="width: 42px; transform: scale(0.85); transform-origin: right center;">
<input type="checkbox" id="lockSelectionToggle">
<span>
<em></em>
<strong></strong>
</span>
</label>
</div>
<button id="selectAllBtn" class="uiverse-btn" title="Select Entire Model"
data-i18n="selectEntireStl">Select Entire STL
File</button>
</div>
<div class="control-row" style="margin-top: 10px; margin-bottom: 10px;">
<button id="clearSelectionBtn" class="uiverse-btn danger" data-i18n="clearSelection"
style="width: 100%;">Clear</button>
</div>
<!-- Validation Warning -->
<div id="selectionWarning" class="warning-toast" style="display: none;" data-i18n="loadStlFirst">Please
load STL first!</div>
<!-- Warning for Poly Check -->
<div id="polyWarning" class="warning-text" style="display: none;" data-i18n="polyLimitReached">Limit
Reached!</div>
</div>
<!-- Texture Mapping Controls (Grid Layout) -->
<div class="panel-section" id="textureControls" style="opacity: 0.5; pointer-events: none;">
<h2 data-i18n="textureMappingTitle">Texture Mapping</h2>
<!-- Pattern Mode Removed -->
<!-- New: Mapping Mode Selection -->
<div class="tooltip-container" style="margin-bottom: 20px; margin-top: 10px;">
<select id="mappingModeSelect" class="modern-select">
<option value="5" data-i18n="modeTriplanar">Triplanar (X, Y, Z Blend)</option>
<option value="6" data-i18n="modeCubic">Cubic (Single Axis)</option>
<option value="4" data-i18n="modeSpherical">Spherical (Polar Wrap)</option>
<option value="3" data-i18n="modeCylindrical">Cylindrical (Radial Wrap)</option>
<option value="0" data-i18n="modePlanarXY">Planar (Front/Back)</option>
<option value="1" data-i18n="modePlanarXZ">Planar (Top/Bottom)</option>
<option value="2" data-i18n="modePlanarYZ">Planar (Left/Right)</option>
</select>
</div>
<!-- Align Projection Button -->
<div class="control-row" style="margin-bottom: 15px;">
<button id="alignProjectionBtn" class="uiverse-btn secondary"
style="width: 100%; font-size: 0.8rem; padding: 6px 0;" data-i18n="alignProjection"
title="Aligns texture projection to currently selected faces">Align to Selection</button>
</div>
<!-- Texture Details Section -->
<div class="texture-details-group" style="margin-bottom: 15px;">
<div class="details-summary" style="cursor: pointer; font-size: 0.85rem; padding: 8px 0; display: flex; align-items: center; justify-content: space-between; user-select: none; outline: none; font-weight: 500; color: var(--text-main);">
<span data-i18n="textureDetails">Details</span>
<svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" class="details-chevron" style="transition: transform 0.3s;"><polyline points="6 9 12 15 18 9"></polyline></svg>
</div>
<div class="details-content">
<div class="details-content-inner" style="margin-top: 10px; display: block;">
<!-- New: Pole Smoothness (Spherical Only) -->
<div id="poleSmoothContainer" class="grid-control tooltip-container" style="display: none;">
<label data-i18n="poleSmooth">Pole Smooth</label>
<input type="range" id="poleSmooth" class="texture-range" min="0" max="1" step="0.01" value="0">
<div class="textInputWrapper">
<input type="text" id="poleSmoothInput" class="textInput" placeholder="0.00">
</div>
<div class="tooltip" data-i18n="poleSmoothTooltip">Fixes pinching at sphere poles.</div>
</div>
<div class="grid-control tooltip-container">
<label data-i18n="scale">Scale</label>
<input type="range" id="texScale" class="texture-range" min="-10" max="10" step="0.1" value="0.0">
<div class="textInputWrapper">
<input type="text" id="texScaleInput" class="textInput" placeholder="0.0">
</div>
<div class="tooltip" data-i18n="scaleTooltip">Global scale of the texture projection.</div>
</div>
<div class="grid-control tooltip-container">
<label data-i18n="amplitude">Amplitude</label>
<input type="range" id="texAmp" class="texture-range" min="-2" max="2" step="0.1" value="0.40">
<div class="textInputWrapper">
<input type="text" id="texAmpInput" class="textInput" placeholder="0.40">
</div>
<div class="tooltip" data-i18n="amplitudeTooltip">Depth/Height of the displacement effect.</div>
</div>
<div class="grid-control tooltip-container">
<label data-i18n="sharpness">Sharpness</label>
<input type="range" id="texSharp" class="texture-range" min="0" max="20" step="0.1" value="10">
<div class="textInputWrapper">
<input type="text" id="texSharpInput" class="textInput" placeholder="10.0">
</div>
<div class="tooltip" data-i18n="sharpnessTooltip">Contrast blend between triplanar projections.
</div>
</div>
<div class="grid-control tooltip-container">
<label data-i18n="offset">Offset</label>
<input type="range" id="texOffset" class="texture-range" min="-10" max="10" step="0.01" value="0">
<div class="textInputWrapper">
<input type="text" id="texOffsetInput" class="textInput" placeholder="0.0">
</div>
<div class="tooltip" data-i18n="offsetTooltip">UV offset for the texture.</div>
</div>
<div class="grid-control tooltip-container">
<label data-i18n="rotation">Rotation</label>
<input type="range" id="texRot" class="texture-range" min="0" max="180" step="1" value="0">
<div class="textInputWrapper">
<input type="text" id="texRotInput" class="textInput" placeholder="0°">
</div>
<div class="tooltip" data-i18n="rotationTooltip">Rotate texture (0-180°).</div>
</div>
<div class="grid-control tooltip-container" style="position: relative;">
<label data-i18n="polyLimit">Max Triangles</label>
<input type="range" id="polyLimit" class="texture-range" min="1" max="9" step="1" value="2">
<div class="textInputWrapper">
<input type="text" id="polyLimitInput" class="textInput" placeholder="2M">
</div>
<div class="tooltip" data-i18n="polyLimitTooltip">Maximum triangle budget. Higher values may slow
down your browser.</div>
<div id="highPolyWarning" class="warning-toast"
style="margin-top: 10px; grid-column: 1 / -1; font-size: 0.80rem;" data-i18n="highPolyWarning">
⚠️ High limits may cause lagging!
</div>
</div>
<div class="grid-control tooltip-container">
<label data-i18n="simplifyIntensity">Simplify Intensity</label>
<input type="range" id="simplifyIntensity" class="texture-range" min="1" max="5" step="1" value="2">
<div class="textInputWrapper">
<input type="text" id="simplifyIntensityInput" class="textInput" placeholder="2">
</div>
<div class="tooltip" data-i18n="simplifyIntensityTooltip">Controls texture decimation (1-5). Unselected regions are fully protected. Flat areas always naturally simplify maximally.</div>
</div>
</div>
</div>
</div>
<div class="button-group" style="margin-top: 15px;">
<button id="applyBtn" class="uiverse-btn warning" disabled data-i18n="applyBake">Apply
(Bake)</button>
<button id="exportBtn" class="btn-donate" disabled title="Save .STL">
<span data-i18n="exportStl">Export STL</span>
</button>
</div>
</div>
<!-- View Options -->
<div class="panel-section">
<div class="section-header-row">
<h2 data-i18n="viewModeTitle">View Mode</h2>
<div class="galahhad-switch-wrapper">
<label class="switch">
<input type="checkbox" id="wireframeToggle">
<span class="slider round"></span>
</label>
<span class="label-text" data-i18n="wireframe">Wireframe</span>
</div>
</div>
</div>
<!-- Stats -->
<div class="panel-section stats-panel">
<div class="stat-row"><span data-i18n="triangles">Triangles:</span> <span id="polyCount">0</span></div>
<div class="stat-row"><span data-i18n="selected">Selected:</span> <span id="selectedCount">0</span>
</div>
<div class="stat-row" style="margin-top: 10px;"><span data-i18n="version">Version:</span>
<span>v1.65</span>
</div>
<div style="margin-top: 15px; font-size: 0.8rem; opacity: 0.7; line-height: 1.4;"
data-i18n="disclaimer">
The site is currently in the trial phase. If you notice any issues, I would appreciate it if you
could contact me.
</div>
</div>
</div>
</aside>
<!-- Main Canvas -->
<main id="canvasContainer">
<div id="welcomeOverlay" class="welcome-overlay">
<h1 data-i18n="overlayTitle">Workflow Guide</h1>
<div class="steps-list">
<div class="step-item"><span class="step-num">1</span> <span class="step-text"><strong
data-i18n="step1Title">Load
Model:</strong> <span data-i18n="step1Desc">Import your .STL file.</span></span></div>
<div class="step-item"><span class="step-num">2</span> <span class="step-text"><strong
data-i18n="step2Title">Select
Surfaces:</strong> <span data-i18n="step2Desc">Click faces to texture.</span></span></div>
<div class="step-item"><span class="step-num">3</span> <span class="step-text"><strong
data-i18n="step4Title">Load
Texture:</strong> <span data-i18n="step4Desc">Upload Map (JPG, PNG, SVG, WEBP,
HTML).</span></span></div>
<div class="step-item"><span class="step-num">4</span> <span class="step-text"><strong
data-i18n="step5Title">Settings:</strong> <span data-i18n="step5Desc">Adjust Scale &
Amplitude.</span></span></div>
<div class="step-item"><span class="step-num">5</span> <span class="step-text"><strong
data-i18n="step6Title">Bake:</strong>
<span data-i18n="step6Desc">Apply changes & Export STL.</span></span></div>
</div>
<div class="overlay-controls">
<h3 data-i18n="overlayControlsTitle">Viewport Controls</h3>
<div class="ctrl-grid">
<div class="ctrl-item"><span class="key" data-i18n="overlayRotateKey">Left Click</span>
<span>↻</span> <span data-i18n="overlayRotate">Rotate</span>
</div>
<div class="ctrl-item"><span class="key" data-i18n="overlayPanKey">Right Click</span> <span>✥</span>
<span data-i18n="overlayPan">Pan</span>
</div>
<div class="ctrl-item"><span class="key" data-i18n="overlayScrollKey">Scroll</span> <span>🔍</span>
<span data-i18n="overlayZoom">Zoom</span>
</div>
</div>
</div>
<div class="overlay-warning"
style="margin-top: 20px; font-size: 0.85rem; color: var(--text-main); text-align: center; background: rgba(255, 165, 0, 0.1); padding: 10px; border-radius: 8px; border: 1px solid rgba(255, 165, 0, 0.3);">
<strong style="color: #ffb74d;">⚠️ <span data-i18n="importantNote">Important Note</span>:</strong> <span
data-i18n="bakeWarning">The baking process depends heavily on your GPU performance. Please be
patient and do not close the window while baking is in progress.</span>
</div>
<div class="overlay-warning"
style="margin-top: 10px; font-size: 0.85rem; color: var(--text-main); text-align: center; background: rgba(33, 150, 243, 0.1); padding: 10px; border-radius: 8px; border: 1px solid rgba(33, 150, 243, 0.3);">
<strong style="color: #64b5f6;">🔒 <span data-i18n="privacyMatters">Your Privacy Matters!</span></strong> <span
data-i18n="privacyDesc">Unlike other tools, MeshTexture does not send your files to any server. Your proprietary designs are processed entirely using your own hardware (CPU/GPU) via your browser.</span>
</div>
</div>
<!-- Scrolling Ticker -->
<div class="ticker-wrap">
<div class="ticker">
<div class="ticker-item" data-i18n="tickerTip1">💡 <strong>PRO TIP:</strong> If your model has curved surfaces (like spheres), try switching to <strong>'Spherical Mode'</strong> for seamless results.</div>
<div class="ticker-item" data-i18n="tickerTip2">⚙️ <strong>QUALITY:</strong> Use Refine Selection to increase face count for sharp displacement.</div>
<div class="ticker-item" data-i18n="tickerTip3">🎨 <strong>FORMATS:</strong> Supports JPG, PNG, WEBP. High contrast images produce the best depth.</div>
<div class="ticker-item" data-i18n="tickerTip4">⚡ <strong>PERFORMANCE:</strong> Baking allows you to export the modified geometry as a standard STL file.</div>
<div class="ticker-item" data-i18n="tickerTip5">🔒 Zero Storage: We don't save or see your models.</div>
<div class="ticker-item" data-i18n="tickerTip6">⚙️ Local Processing: 100% client-side execution.</div>
<div class="ticker-item" data-i18n="tickerTip1">💡 <strong>PRO TIP:</strong> If your model has curved surfaces (like spheres), try switching to <strong>'Spherical Mode'</strong> for seamless results.</div>
<div class="ticker-item" data-i18n="tickerTip2">⚙️ <strong>QUALITY:</strong> Use Refine Selection to increase face count for sharp displacement.</div>
<div class="ticker-item" data-i18n="tickerTip3">🎨 <strong>FORMATS:</strong> Supports JPG, PNG, WEBP. High contrast images produce the best depth.</div>
<div class="ticker-item" data-i18n="tickerTip4">⚡ <strong>PERFORMANCE:</strong> Baking allows you to export the modified geometry as a standard STL file.</div>
<div class="ticker-item" data-i18n="tickerTip5">🔒 Zero Storage: We don't save or see your models.</div>
<div class="ticker-item" data-i18n="tickerTip6">⚙️ Local Processing: 100% client-side execution.</div>
</div>
</div>
<!-- Three.js Canvas will be injected here -->
<div id="viewCubeContainer" class="view-cube-container">
<button id="viewCubeHomeBtn" class="view-cube-home-btn" title="Home View">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path>
<polyline points="9 22 9 12 15 12 15 22"></polyline>
</svg>
</button>
</div>
</main>
<!-- Global Loading Overlay -->
<div id="loadingOverlay" class="spinner-overlay" style="display: none;">
<div class="spinner">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
<!-- Status Container for Sliding Animation -->
<div class="status-container">
<div id="statusCurrent" class="status-line current"></div>
<div id="statusNext" class="status-line next"></div>
</div>
<div class="progress-container">
<div id="bakeProgressBar" class="progress-bar"></div>
</div>
<div class="warning-subtext" data-i18n="dontCloseTab">Don't close tab</div>
</div>
<!-- Processing Overlay -->
<div id="processingOverlay" class="processing-overlay"></div>
<!-- Module Entry -->
<script type="module" src="./src/main.js"></script>
<!-- Moving Preset Popover to Body Root to prevent clipping -->
<div id="presetPopover" class="preset-popover">
<div class="preset-header" data-i18n="presetTextures">Preset Textures</div>
<div class="preset-category" data-i18n="catGeometric">Geometric</div>
<div class="preset-grid">
<div class="preset-item" data-src="assets/textures/carbon_fiber.webp" title="Carbon Fiber">
<img loading="lazy" decoding="async" src="assets/textures/carbon_fiber.webp" alt="Carbon Fiber">
<span data-i18n="presetCarbon">Carbon Fiber</span>
</div>
<div class="preset-item" data-src="assets/textures/Grip_1.webp" title="Grip 1">
<img loading="lazy" decoding="async" src="assets/textures/Grip_1.webp" alt="Grip 1">
<span data-i18n="presetGrip1">Grip 1</span>
</div>
<div class="preset-item" data-src="assets/textures/Grip_2.webp" title="Grip 2">
<img loading="lazy" decoding="async" src="assets/textures/Grip_2.webp" alt="Grip 2">
<span data-i18n="presetGrip2">Grip 2</span>
</div>
<div class="preset-item" data-src="assets/textures/hexagon.webp" title="Hexagon">
<img loading="lazy" decoding="async" src="assets/textures/hexagon.webp" alt="Hexagon">
<span data-i18n="presetHexagon">Hexagon</span>
</div>
<div class="preset-item" data-src="assets/textures/geo.webp" title="Geo">
<img loading="lazy" decoding="async" src="assets/textures/geo.webp" alt="Geo">
<span data-i18n="presetGeo">Geo</span>
</div>
</div>
<div class="preset-category" data-i18n="catOrganic">Organic</div>
<div class="preset-grid">
<div class="preset-item" data-src="assets/textures/leather.webp" title="Leather">
<img loading="lazy" decoding="async" src="assets/textures/leather.webp" alt="Leather">
<span data-i18n="presetLeather">Leather</span>
</div>
<div class="preset-item" data-src="assets/textures/wood.webp" title="Wood">
<img loading="lazy" decoding="async" src="assets/textures/wood.webp" alt="Wood">
<span data-i18n="presetWood">Wood</span>
</div>
<div class="preset-item" data-src="assets/textures/wood_2.webp" title="Wood 2">
<img loading="lazy" decoding="async" src="assets/textures/wood_2.webp" alt="Wood 2">
<span data-i18n="presetWood2">Wood 2</span>
</div>
<div class="preset-item" data-src="assets/textures/wood_3.webp" title="Wood 3">
<img loading="lazy" decoding="async" src="assets/textures/wood_3.webp" alt="Wood 3">
<span data-i18n="presetWood3">Wood 3</span>
</div>
<div class="preset-item" data-src="assets/textures/cement.webp" title="Cement">
<img loading="lazy" decoding="async" src="assets/textures/cement.webp" alt="Cement">
<span data-i18n="presetCement">Cement</span>
</div>
<div class="preset-item" data-src="assets/textures/brick.webp" title="Brick">
<img loading="lazy" decoding="async" src="assets/textures/brick.webp" alt="Brick">
<span data-i18n="presetBrick">Brick</span>
</div>
<div class="preset-item" data-src="assets/textures/leaf.webp" title="Leaf">
<img loading="lazy" decoding="async" src="assets/textures/leaf.webp" alt="Leaf">
<span data-i18n="presetLeaf">Leaf</span>
</div>
</div>
</div>
<!-- Paint Popover (Initially Hidden) -->
<div id="paintPopover" class="preset-popover paint-popover">
<div class="preset-header" data-i18n="paintSettings">Paint Settings</div>
<div data-i18n="paintInstructions"
style="font-size:0.75rem; color:var(--text-dim); margin-bottom: 15px; line-height: 1.4; text-align:center;">
Hold <b>E</b> + Left Click to <b>Paint</b><br>
Hold <b>R</b> + Left Click to <b>Erase</b>
</div>
<div class="control-row tooltip-container slider-row-horizontal" style="margin-bottom: 12px;">
<label data-i18n="brushSize">Brush Size</label>
<div class="range-slider-wrapper">
<div class="value-bubble" id="paintBrushBubble" style="left:20%">3</div>
<input type="range" id="paintBrushSize" class="modern-range" min="1" max="10" value="3">
</div>
</div>
<div class="control-row tooltip-container slider-row-horizontal" style="margin-bottom: 12px;">
<label data-i18n="paintTolerance">Angle Tolerance</label>
<div class="range-slider-wrapper">
<div class="value-bubble" id="paintAngleBubble" style="left:50%">45</div>
<input type="range" id="paintAngleThreshold" class="modern-range" min="1" max="90" value="45">
</div>
</div>
<div class="control-row"
style="margin-bottom: 12px; display:flex; justify-content:space-between; align-items:center;">
<label data-i18n="ignoreBackfacing" style="font-size: 0.8rem; color:var(--text-main); margin:0;">Ignore
Backfacing</label>
<label class="switch" style="transform: scale(0.85); margin:0;">
<input type="checkbox" id="paintIgnoreBackfacing" checked>
<span class="slider round"></span>
</label>
</div>
<button id="paintInvertBtn" class="uiverse-btn secondary" style="width:100%; font-size: 0.8rem; padding: 6px 0;"
data-i18n="invertSelection">Invert Selection</button>
</div>
<!-- SEO Semantic Section: visible to search bots, visually unobtrusive -->
<section id="seo-content" aria-label="About this tool"
style="position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;">
<h1>Free Online 3D Mesh Texturing Tool</h1>
<p>Mesh Texture Pro is a browser-based application that lets you apply procedural 3D textures directly onto STL
mesh files — no installation required.</p>
<h2>How to apply 3D textures to STL files?</h2>
<img src="assets/images/logov1.png" alt="Mesh Texture Pro brand logo" width="64" height="64"
style="display:block;margin:8px 0;">
<ol>
<li><strong>Load your STL file</strong> — Import any standard binary or ASCII STL model into the viewport.
</li>
<li><strong>Select surfaces & choose a texture</strong> — Click the faces you want to emboss, then pick
a preset texture (carbon fiber, hexagon, knurling…) or upload your own JPG/PNG.</li>
<li><strong>Bake & export</strong> — Adjust scale, amplitude, and rotation, then hit Apply (Bake) to
displace the geometry and download the modified STL file ready for 3D printing.</li>
</ol>
</section>
</body>
</html>