meshwriter-cudu 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +11 -0
- package/README.md +349 -0
- package/dist/fonts/comic-sans.d.ts +1105 -0
- package/dist/fonts/helvetica.d.ts +1208 -0
- package/dist/fonts/hiruko-pro.d.ts +658 -0
- package/dist/fonts/jura.d.ts +750 -0
- package/dist/fonts/webgl-dings.d.ts +109 -0
- package/dist/index.d.ts +295 -0
- package/dist/meshwriter.cjs.js +2645 -0
- package/dist/meshwriter.cjs.js.map +1 -0
- package/dist/meshwriter.esm.js +2606 -0
- package/dist/meshwriter.esm.js.map +1 -0
- package/dist/meshwriter.min.js +2 -0
- package/dist/meshwriter.min.js.map +1 -0
- package/dist/meshwriter.umd.js +7146 -0
- package/dist/meshwriter.umd.js.map +1 -0
- package/dist/src/babylonImports.d.ts +11 -0
- package/dist/src/bakedFontLoader.d.ts +43 -0
- package/dist/src/colorContrast.d.ts +117 -0
- package/dist/src/csg.d.ts +55 -0
- package/dist/src/curves.d.ts +20 -0
- package/dist/src/fogPlugin.d.ts +32 -0
- package/dist/src/fontCompression.d.ts +12 -0
- package/dist/src/fontRegistry.d.ts +54 -0
- package/dist/src/index.d.ts +47 -0
- package/dist/src/letterMesh.d.ts +46 -0
- package/dist/src/material.d.ts +34 -0
- package/dist/src/meshSplitter.d.ts +10 -0
- package/dist/src/meshwriter.d.ts +46 -0
- package/dist/src/sps.d.ts +27 -0
- package/dist/src/umd-entry.d.ts +3 -0
- package/dist/src/utils.d.ts +12 -0
- package/dist/src/variableFontCache.d.ts +56 -0
- package/dist/src/variableFontConverter.d.ts +21 -0
- package/dist/src/variableFontLoader.d.ts +99 -0
- package/fonts/Figure1.png +0 -0
- package/fonts/LICENSE-OFL.txt +93 -0
- package/fonts/README.md +174 -0
- package/fonts/atkinson-hyperlegible-next.d.ts +8 -0
- package/fonts/atkinson-hyperlegible-next.js +6576 -0
- package/fonts/atkinson-hyperlegible.js +3668 -0
- package/fonts/baked/atkinson-hyperlegible-next-200.json +1 -0
- package/fonts/baked/atkinson-hyperlegible-next-250.json +1 -0
- package/fonts/baked/atkinson-hyperlegible-next-300.json +1 -0
- package/fonts/baked/atkinson-hyperlegible-next-350.json +1 -0
- package/fonts/baked/atkinson-hyperlegible-next-400.json +1 -0
- package/fonts/baked/atkinson-hyperlegible-next-450.json +1 -0
- package/fonts/baked/atkinson-hyperlegible-next-500.json +1 -0
- package/fonts/baked/atkinson-hyperlegible-next-550.json +1 -0
- package/fonts/baked/atkinson-hyperlegible-next-600.json +1 -0
- package/fonts/baked/atkinson-hyperlegible-next-650.json +1 -0
- package/fonts/baked/atkinson-hyperlegible-next-700.json +1 -0
- package/fonts/baked/atkinson-hyperlegible-next-750.json +1 -0
- package/fonts/baked/atkinson-hyperlegible-next-800.json +1 -0
- package/fonts/baked/manifest.json +41 -0
- package/fonts/comic-sans.js +1532 -0
- package/fonts/helvetica.js +1695 -0
- package/fonts/hiruko-pro.js +838 -0
- package/fonts/index.js +16 -0
- package/fonts/jura.js +994 -0
- package/fonts/variable/atkinson-hyperlegible-next-variable.ttf +0 -0
- package/fonts/webgl-dings.js +113 -0
- package/package.json +76 -0
- package/src/babylonImports.js +29 -0
- package/src/bakedFontLoader.js +125 -0
- package/src/colorContrast.js +528 -0
- package/src/csg.js +220 -0
- package/src/curves.js +67 -0
- package/src/fogPlugin.js +98 -0
- package/src/fontCompression.js +141 -0
- package/src/fontRegistry.js +98 -0
- package/src/globals.d.ts +20 -0
- package/src/index.js +136 -0
- package/src/letterMesh.js +417 -0
- package/src/material.js +103 -0
- package/src/meshSplitter.js +337 -0
- package/src/meshwriter.js +303 -0
- package/src/sps.js +106 -0
- package/src/types.d.ts +551 -0
- package/src/umd-entry.js +130 -0
- package/src/utils.js +57 -0
package/src/csg.js
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MeshWriter CSG Module
|
|
3
|
+
* Handles CSG version detection, async initialization, and ready state management
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { isObject, isPromiseLike } from './utils.js';
|
|
7
|
+
import {
|
|
8
|
+
CSG as BabylonCSG,
|
|
9
|
+
CSG2 as BabylonCSG2,
|
|
10
|
+
InitializeCSG2Async as BabylonInitializeCSG2Async,
|
|
11
|
+
IsCSG2Ready as BabylonIsCSG2Ready
|
|
12
|
+
} from './babylonImports.js';
|
|
13
|
+
|
|
14
|
+
// CSG libraries - initialized from Babylon imports or external source
|
|
15
|
+
let CSG = BabylonCSG;
|
|
16
|
+
let CSG2 = BabylonCSG2;
|
|
17
|
+
let InitializeCSG2Async = BabylonInitializeCSG2Async;
|
|
18
|
+
let IsCSG2Ready = BabylonIsCSG2Ready;
|
|
19
|
+
|
|
20
|
+
// State
|
|
21
|
+
let csgVersion = null; // 'CSG2', 'CSG', or null
|
|
22
|
+
let csgReady = false;
|
|
23
|
+
const csgReadyListeners = [];
|
|
24
|
+
let externalCSGInitializer = null;
|
|
25
|
+
let externalCSGReadyCheck = null;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Initialize CSG module with Babylon.js CSG classes
|
|
29
|
+
* Must be called before using any CSG functionality
|
|
30
|
+
* @param {Object} babylon - Object containing CSG, CSG2, InitializeCSG2Async, IsCSG2Ready
|
|
31
|
+
*/
|
|
32
|
+
export function initCSGModule(babylon) {
|
|
33
|
+
if (isObject(babylon)) {
|
|
34
|
+
CSG = babylon.CSG || null;
|
|
35
|
+
CSG2 = babylon.CSG2 || null;
|
|
36
|
+
InitializeCSG2Async = babylon.InitializeCSG2Async || null;
|
|
37
|
+
IsCSG2Ready = babylon.IsCSG2Ready || null;
|
|
38
|
+
}
|
|
39
|
+
csgVersion = detectCSGVersion();
|
|
40
|
+
|
|
41
|
+
// Legacy CSG is immediately ready
|
|
42
|
+
if (csgVersion === 'CSG') {
|
|
43
|
+
markCSGInitialized();
|
|
44
|
+
} else if (csgVersion === 'CSG2') {
|
|
45
|
+
// Check if CSG2 is already initialized
|
|
46
|
+
csgReady = false;
|
|
47
|
+
if (runCSGReadyCheck()) {
|
|
48
|
+
markCSGInitialized();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Detect which CSG implementation is available
|
|
55
|
+
* @returns {'CSG2'|'CSG'|null}
|
|
56
|
+
*/
|
|
57
|
+
export function detectCSGVersion() {
|
|
58
|
+
// Prefer CSG2 (Babylon 7.31+)
|
|
59
|
+
if (isObject(CSG2) && typeof InitializeCSG2Async === 'function') {
|
|
60
|
+
return 'CSG2';
|
|
61
|
+
}
|
|
62
|
+
// Fall back to legacy CSG
|
|
63
|
+
if (isObject(CSG) && typeof CSG.FromMesh === 'function') {
|
|
64
|
+
return 'CSG';
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Mark CSG as ready and notify all listeners
|
|
71
|
+
*/
|
|
72
|
+
export function markCSGInitialized() {
|
|
73
|
+
if (csgReady) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
csgReady = true;
|
|
77
|
+
|
|
78
|
+
// Notify all waiting listeners
|
|
79
|
+
if (csgReadyListeners.length) {
|
|
80
|
+
csgReadyListeners.splice(0).forEach(function(listener) {
|
|
81
|
+
try {
|
|
82
|
+
listener();
|
|
83
|
+
} catch (err) {
|
|
84
|
+
console.error("MeshWriter: onCSGReady listener failed", err);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Check if CSG is ready for use
|
|
92
|
+
* @returns {boolean}
|
|
93
|
+
*/
|
|
94
|
+
export function isCSGReady() {
|
|
95
|
+
if (csgVersion === 'CSG2') {
|
|
96
|
+
refreshCSGReadyState();
|
|
97
|
+
return csgReady;
|
|
98
|
+
}
|
|
99
|
+
return csgVersion === 'CSG';
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Refresh CSG2 ready state from external checks
|
|
104
|
+
*/
|
|
105
|
+
function refreshCSGReadyState() {
|
|
106
|
+
if (csgVersion !== 'CSG2' || csgReady) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (runCSGReadyCheck()) {
|
|
110
|
+
markCSGInitialized();
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Run CSG ready check (external or native)
|
|
116
|
+
*/
|
|
117
|
+
function runCSGReadyCheck() {
|
|
118
|
+
// Try external ready check first
|
|
119
|
+
if (typeof externalCSGReadyCheck === "function") {
|
|
120
|
+
try {
|
|
121
|
+
if (externalCSGReadyCheck()) {
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
} catch (err) {
|
|
125
|
+
console.warn("MeshWriter: external CSG ready check failed", err);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Check native IsCSG2Ready
|
|
130
|
+
if (typeof IsCSG2Ready === "function" && IsCSG2Ready()) {
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Initialize CSG2 asynchronously
|
|
139
|
+
* @returns {Promise<void>}
|
|
140
|
+
*/
|
|
141
|
+
export async function initializeCSG2() {
|
|
142
|
+
if (csgVersion !== 'CSG2') {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
if (csgReady) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const initializer = externalCSGInitializer || InitializeCSG2Async;
|
|
150
|
+
if (typeof initializer !== "function") {
|
|
151
|
+
throw new Error(
|
|
152
|
+
"MeshWriter: No CSG2 initializer available. " +
|
|
153
|
+
"Use MeshWriter.setCSGInitializer() or ensure BABYLON.InitializeCSG2Async is available."
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const result = initializer();
|
|
158
|
+
if (isPromiseLike(result)) {
|
|
159
|
+
await result;
|
|
160
|
+
}
|
|
161
|
+
markCSGInitialized();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Public API
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Get the current CSG version being used
|
|
168
|
+
* @returns {'CSG2'|'CSG'|null}
|
|
169
|
+
*/
|
|
170
|
+
export function getCSGVersion() {
|
|
171
|
+
return csgVersion;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Set an external CSG2 initializer function
|
|
176
|
+
* @param {Function} fn - Async function that initializes CSG2
|
|
177
|
+
*/
|
|
178
|
+
export function setCSGInitializer(fn) {
|
|
179
|
+
if (typeof fn === "function") {
|
|
180
|
+
externalCSGInitializer = fn;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Set an external CSG2 ready check function
|
|
186
|
+
* @param {Function} fn - Function that returns true when CSG2 is ready
|
|
187
|
+
*/
|
|
188
|
+
export function setCSGReadyCheck(fn) {
|
|
189
|
+
if (typeof fn === "function") {
|
|
190
|
+
externalCSGReadyCheck = fn;
|
|
191
|
+
refreshCSGReadyState();
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Register a callback to be called when CSG is ready
|
|
197
|
+
* If CSG is already ready, callback is called immediately
|
|
198
|
+
* @param {Function} listener
|
|
199
|
+
*/
|
|
200
|
+
export function onCSGReady(listener) {
|
|
201
|
+
if (typeof listener !== "function") {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
if (isCSGReady()) {
|
|
205
|
+
listener();
|
|
206
|
+
} else {
|
|
207
|
+
csgReadyListeners.push(listener);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Get the CSG library to use for operations
|
|
213
|
+
* @returns {Object} - CSG or CSG2 class
|
|
214
|
+
*/
|
|
215
|
+
export function getCSGLib() {
|
|
216
|
+
return csgVersion === 'CSG2' ? CSG2 : CSG;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Export for direct access when needed
|
|
220
|
+
export { CSG, CSG2 };
|
package/src/curves.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MeshWriter Curve Extensions
|
|
3
|
+
* Optimized Path2 curve methods for better performance
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Path2, Curve3, Vector3 } from './babylonImports.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Extended Path2 interface with MeshWriter curve methods
|
|
10
|
+
* @typedef {Object} Path2Extensions
|
|
11
|
+
* @property {(redX: number, redY: number, blueX: number, blueY: number) => Path2} addQuadraticCurveTo
|
|
12
|
+
* @property {(redX: number, redY: number, greenX: number, greenY: number, blueX: number, blueY: number) => Path2} addCubicCurveTo
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
// Optimized segment count for curves
|
|
16
|
+
// Native Babylon 6+ uses 36 segments which causes slowdown
|
|
17
|
+
// MeshWriter uses 6 for better performance
|
|
18
|
+
export const curveSampleSize = 6;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Install optimized curve methods on Path2 prototype
|
|
22
|
+
* Must be called after Babylon.js is loaded
|
|
23
|
+
*/
|
|
24
|
+
export function installCurveExtensions() {
|
|
25
|
+
if (!Path2 || !Path2.prototype) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** @type {any} */
|
|
30
|
+
const proto = Path2.prototype;
|
|
31
|
+
|
|
32
|
+
// Quadratic Bezier with optimized segment count
|
|
33
|
+
proto.addQuadraticCurveTo = function(redX, redY, blueX, blueY) {
|
|
34
|
+
const points = this.getPoints();
|
|
35
|
+
const lastPoint = points[points.length - 1];
|
|
36
|
+
const origin = new Vector3(lastPoint.x, lastPoint.y, 0);
|
|
37
|
+
const control = new Vector3(redX, redY, 0);
|
|
38
|
+
const destination = new Vector3(blueX, blueY, 0);
|
|
39
|
+
|
|
40
|
+
const curve = Curve3.CreateQuadraticBezier(origin, control, destination, curveSampleSize);
|
|
41
|
+
const curvePoints = curve.getPoints();
|
|
42
|
+
|
|
43
|
+
for (let i = 1; i < curvePoints.length; i++) {
|
|
44
|
+
this.addLineTo(curvePoints[i].x, curvePoints[i].y);
|
|
45
|
+
}
|
|
46
|
+
return this; // Return this for method chaining
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// Cubic Bezier with optimized segment count
|
|
50
|
+
proto.addCubicCurveTo = function(redX, redY, greenX, greenY, blueX, blueY) {
|
|
51
|
+
const points = this.getPoints();
|
|
52
|
+
const lastPoint = points[points.length - 1];
|
|
53
|
+
const origin = new Vector3(lastPoint.x, lastPoint.y, 0);
|
|
54
|
+
const control1 = new Vector3(redX, redY, 0);
|
|
55
|
+
const control2 = new Vector3(greenX, greenY, 0);
|
|
56
|
+
const destination = new Vector3(blueX, blueY, 0);
|
|
57
|
+
|
|
58
|
+
const nbPoints = Math.floor(0.3 + curveSampleSize * 1.5);
|
|
59
|
+
const curve = Curve3.CreateCubicBezier(origin, control1, control2, destination, nbPoints);
|
|
60
|
+
const curvePoints = curve.getPoints();
|
|
61
|
+
|
|
62
|
+
for (let i = 1; i < curvePoints.length; i++) {
|
|
63
|
+
this.addLineTo(curvePoints[i].x, curvePoints[i].y);
|
|
64
|
+
}
|
|
65
|
+
return this; // Return this for method chaining
|
|
66
|
+
};
|
|
67
|
+
}
|
package/src/fogPlugin.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TextFogPlugin - MaterialPluginBase that applies fog to emissive color
|
|
3
|
+
*
|
|
4
|
+
* Babylon's standard fog only affects diffuse/ambient channels.
|
|
5
|
+
* This plugin recalculates fog blending for the final color output,
|
|
6
|
+
* ensuring emissive text fades properly with distance fog.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { MaterialPluginBase } from './babylonImports.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Plugin that applies scene fog to text materials by modifying
|
|
13
|
+
* the final fragment color before output.
|
|
14
|
+
*/
|
|
15
|
+
export class TextFogPlugin extends MaterialPluginBase {
|
|
16
|
+
/**
|
|
17
|
+
* @param {import('@babylonjs/core/Materials/material').Material} material
|
|
18
|
+
*/
|
|
19
|
+
constructor(material) {
|
|
20
|
+
var priority = 300; // Run after standard material processing
|
|
21
|
+
var defines = { 'MESHWRITER_TEXT_FOG': false };
|
|
22
|
+
super(material, 'TextFogPlugin', priority, defines);
|
|
23
|
+
this._enable(true);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Set the define based on whether scene fog is enabled
|
|
28
|
+
* @param {object} defines
|
|
29
|
+
* @param {import('@babylonjs/core/scene').Scene} scene
|
|
30
|
+
* @param {import('@babylonjs/core/Meshes/mesh').Mesh} mesh
|
|
31
|
+
*/
|
|
32
|
+
prepareDefines(defines, scene, mesh) {
|
|
33
|
+
// Enable when scene has any fog mode set (1=LINEAR, 2=EXP, 3=EXP2)
|
|
34
|
+
defines['MESHWRITER_TEXT_FOG'] = scene.fogMode !== 0;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
getClassName() {
|
|
38
|
+
return 'TextFogPlugin';
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
getUniforms() {
|
|
42
|
+
// We use Babylon's built-in fog uniforms (vFogInfos, vFogColor)
|
|
43
|
+
// which are already available in the standard material
|
|
44
|
+
return { ubo: [] };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Clean up the plugin
|
|
49
|
+
*/
|
|
50
|
+
dispose() {
|
|
51
|
+
super.dispose();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Inject shader code to apply fog to emissive color
|
|
56
|
+
* @param {string} shaderType - 'vertex' or 'fragment'
|
|
57
|
+
*/
|
|
58
|
+
getCustomCode(shaderType) {
|
|
59
|
+
if (shaderType === 'fragment') {
|
|
60
|
+
return {
|
|
61
|
+
// This injection point runs just before gl_FragColor is finalized
|
|
62
|
+
// At this point, standard fog has been applied to diffuse/ambient
|
|
63
|
+
// but emissive contribution bypasses fog, so we re-apply fog
|
|
64
|
+
// to the entire output to properly fade text into fog
|
|
65
|
+
'CUSTOM_FRAGMENT_BEFORE_FRAGCOLOR': `
|
|
66
|
+
#ifdef MESHWRITER_TEXT_FOG
|
|
67
|
+
#ifdef FOG
|
|
68
|
+
// Recalculate fog for the full fragment color including emissive
|
|
69
|
+
// vFogInfos: x=fogMode, y=fogStart, z=fogEnd, w=fogDensity
|
|
70
|
+
// vFogColor: fog RGB color
|
|
71
|
+
// vFogDistance: vec3 distance from camera (set by vertex shader)
|
|
72
|
+
|
|
73
|
+
float textFogFactor = 1.0;
|
|
74
|
+
float textFogDist = length(vFogDistance);
|
|
75
|
+
|
|
76
|
+
if (FOGMODE_LINEAR == vFogInfos.x) {
|
|
77
|
+
// Linear fog: factor = (end - dist) / (end - start)
|
|
78
|
+
textFogFactor = clamp((vFogInfos.z - textFogDist) / (vFogInfos.z - vFogInfos.y), 0.0, 1.0);
|
|
79
|
+
} else if (FOGMODE_EXP == vFogInfos.x) {
|
|
80
|
+
// Exponential fog: factor = exp(-dist * density)
|
|
81
|
+
textFogFactor = clamp(exp(-textFogDist * vFogInfos.w), 0.0, 1.0);
|
|
82
|
+
} else if (FOGMODE_EXP2 == vFogInfos.x) {
|
|
83
|
+
// Exponential squared fog: factor = exp(-(dist * density)^2)
|
|
84
|
+
float fogDistDensity = textFogDist * vFogInfos.w;
|
|
85
|
+
textFogFactor = clamp(exp(-fogDistDensity * fogDistDensity), 0.0, 1.0);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Blend the entire fragment (including emissive) toward fog color
|
|
89
|
+
// textFogFactor: 1.0 = no fog (full color), 0.0 = full fog
|
|
90
|
+
color.rgb = mix(vFogColor, color.rgb, textFogFactor);
|
|
91
|
+
#endif
|
|
92
|
+
#endif
|
|
93
|
+
`,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MeshWriter Font Compression/Decompression
|
|
3
|
+
* Base-128 encoding for font data compression (~50% size reduction)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { isArray } from './utils.js';
|
|
7
|
+
|
|
8
|
+
const floor = Math.floor;
|
|
9
|
+
|
|
10
|
+
// Encoding arrays (initialized once at module load)
|
|
11
|
+
let b128back;
|
|
12
|
+
let b128digits;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Initialize encoding arrays for base-128 conversion
|
|
16
|
+
* Called once when module loads
|
|
17
|
+
*/
|
|
18
|
+
function prepArray() {
|
|
19
|
+
let pntr = -1;
|
|
20
|
+
let n;
|
|
21
|
+
b128back = new Uint8Array(256);
|
|
22
|
+
b128digits = new Array(128);
|
|
23
|
+
|
|
24
|
+
while (160 > pntr++) {
|
|
25
|
+
if (pntr < 128) {
|
|
26
|
+
n = fr128to256(pntr);
|
|
27
|
+
b128digits[pntr] = String.fromCharCode(n);
|
|
28
|
+
b128back[n] = pntr;
|
|
29
|
+
} else {
|
|
30
|
+
if (pntr === 128) {
|
|
31
|
+
b128back[32] = pntr;
|
|
32
|
+
} else {
|
|
33
|
+
b128back[pntr + 71] = pntr;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function fr128to256(n) {
|
|
39
|
+
if (n < 92) {
|
|
40
|
+
return n < 58 ? n < 6 ? n + 33 : n + 34 : n + 35;
|
|
41
|
+
} else {
|
|
42
|
+
return n + 69;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Initialize on module load
|
|
48
|
+
prepArray();
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Convert base-128 encoded string to number
|
|
52
|
+
*/
|
|
53
|
+
function frB128(s) {
|
|
54
|
+
let result = 0;
|
|
55
|
+
let i = -1;
|
|
56
|
+
const l = s.length - 1;
|
|
57
|
+
while (i++ < l) {
|
|
58
|
+
result = result * 128 + b128back[s.charCodeAt(i)];
|
|
59
|
+
}
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Convert number to base-128 encoded string
|
|
65
|
+
*/
|
|
66
|
+
function toB128(i) {
|
|
67
|
+
let s = b128digits[(i % 128)];
|
|
68
|
+
i = floor(i / 128);
|
|
69
|
+
while (i > 0) {
|
|
70
|
+
s = b128digits[(i % 128)] + s;
|
|
71
|
+
i = floor(i / 128);
|
|
72
|
+
}
|
|
73
|
+
return s;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Decode a compressed command list string
|
|
78
|
+
* @param {string} str - Compressed string (space-separated encoded commands)
|
|
79
|
+
* @returns {Array} - Array of decoded command arrays
|
|
80
|
+
*/
|
|
81
|
+
export function decodeList(str) {
|
|
82
|
+
const split = str.split(" ");
|
|
83
|
+
const list = [];
|
|
84
|
+
|
|
85
|
+
split.forEach(function(cmds) {
|
|
86
|
+
if (cmds.length === 12) { list.push(decode6(cmds)); }
|
|
87
|
+
if (cmds.length === 8) { list.push(decode4(cmds)); }
|
|
88
|
+
if (cmds.length === 4) { list.push(decode2(cmds)); }
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
return list;
|
|
92
|
+
|
|
93
|
+
function decode6(s) {
|
|
94
|
+
return [
|
|
95
|
+
decode1(s, 0, 2), decode1(s, 2, 4), decode1(s, 4, 6),
|
|
96
|
+
decode1(s, 6, 8), decode1(s, 8, 10), decode1(s, 10, 12)
|
|
97
|
+
];
|
|
98
|
+
}
|
|
99
|
+
function decode4(s) {
|
|
100
|
+
return [decode1(s, 0, 2), decode1(s, 2, 4), decode1(s, 4, 6), decode1(s, 6, 8)];
|
|
101
|
+
}
|
|
102
|
+
function decode2(s) {
|
|
103
|
+
return [decode1(s, 0, 2), decode1(s, 2, 4)];
|
|
104
|
+
}
|
|
105
|
+
function decode1(s, start, end) {
|
|
106
|
+
return (frB128(s.substring(start, end)) - 4000) / 2;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Encode a command list to compressed string
|
|
112
|
+
* @param {Array} list - Array of command arrays
|
|
113
|
+
* @returns {string} - Compressed string
|
|
114
|
+
*/
|
|
115
|
+
export function codeList(list) {
|
|
116
|
+
let str = "";
|
|
117
|
+
let xtra = "";
|
|
118
|
+
|
|
119
|
+
if (isArray(list)) {
|
|
120
|
+
list.forEach(function(cmds) {
|
|
121
|
+
if (cmds.length === 6) { str += xtra + code6(cmds); xtra = " "; }
|
|
122
|
+
if (cmds.length === 4) { str += xtra + code4(cmds); xtra = " "; }
|
|
123
|
+
if (cmds.length === 2) { str += xtra + code2(cmds); xtra = " "; }
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return str;
|
|
128
|
+
|
|
129
|
+
function code6(a) {
|
|
130
|
+
return code1(a[0]) + code1(a[1]) + code1(a[2]) + code1(a[3]) + code1(a[4]) + code1(a[5]);
|
|
131
|
+
}
|
|
132
|
+
function code4(a) {
|
|
133
|
+
return code1(a[0]) + code1(a[1]) + code1(a[2]) + code1(a[3]);
|
|
134
|
+
}
|
|
135
|
+
function code2(a) {
|
|
136
|
+
return code1(a[0]) + code1(a[1]);
|
|
137
|
+
}
|
|
138
|
+
function code1(n) {
|
|
139
|
+
return toB128((n + n) + 4000);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MeshWriter Font Registry
|
|
3
|
+
* Manages font registration and lookup
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { isObject } from './utils.js';
|
|
7
|
+
import { codeList, decodeList } from './fontCompression.js';
|
|
8
|
+
|
|
9
|
+
// Private font storage
|
|
10
|
+
const FONTS = {};
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Register a font for use with MeshWriter
|
|
14
|
+
* @param {string} name - Font name (case-sensitive, used in "font-family" option)
|
|
15
|
+
* @param {Function|Object} fontData - Font factory function or pre-initialized font object
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* // Register a font factory (receives codeList for encoding)
|
|
19
|
+
* import helvetica from 'meshwriter/fonts/helvetica';
|
|
20
|
+
* registerFont('Helvetica', helvetica);
|
|
21
|
+
*
|
|
22
|
+
* // Register with aliases
|
|
23
|
+
* registerFont('Arial', helvetica);
|
|
24
|
+
* registerFont('sans-serif', helvetica);
|
|
25
|
+
*/
|
|
26
|
+
export function registerFont(name, fontData) {
|
|
27
|
+
if (typeof fontData === 'function') {
|
|
28
|
+
// Font is a factory function expecting codeList
|
|
29
|
+
FONTS[name] = fontData(codeList);
|
|
30
|
+
} else if (isObject(fontData)) {
|
|
31
|
+
// Font is already initialized
|
|
32
|
+
FONTS[name] = fontData;
|
|
33
|
+
} else {
|
|
34
|
+
throw new Error(`MeshWriter: Invalid font data for "${name}"`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Register multiple font aliases pointing to the same font
|
|
40
|
+
* @param {string} targetName - Name of already-registered font
|
|
41
|
+
* @param {...string} aliases - Alias names to register
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* registerFont('Helvetica', helveticaData);
|
|
45
|
+
* registerFontAliases('Helvetica', 'Arial', 'sans-serif');
|
|
46
|
+
*/
|
|
47
|
+
export function registerFontAliases(targetName, ...aliases) {
|
|
48
|
+
if (!FONTS[targetName]) {
|
|
49
|
+
throw new Error(`MeshWriter: Cannot create aliases: font "${targetName}" not registered`);
|
|
50
|
+
}
|
|
51
|
+
aliases.forEach(alias => {
|
|
52
|
+
FONTS[alias] = FONTS[targetName];
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Get a registered font by name
|
|
58
|
+
* @param {string} name - Font name
|
|
59
|
+
* @returns {Object|undefined} - Font object or undefined if not found
|
|
60
|
+
*/
|
|
61
|
+
export function getFont(name) {
|
|
62
|
+
return FONTS[name];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Check if a font is registered
|
|
67
|
+
* @param {string} name - Font name
|
|
68
|
+
* @returns {boolean}
|
|
69
|
+
*/
|
|
70
|
+
export function isFontRegistered(name) {
|
|
71
|
+
return isObject(FONTS[name]);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Get list of all registered font names
|
|
76
|
+
* @returns {string[]}
|
|
77
|
+
*/
|
|
78
|
+
export function getRegisteredFonts() {
|
|
79
|
+
return Object.keys(FONTS);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Unregister a font (mainly for testing)
|
|
84
|
+
* @param {string} name - Font name to remove
|
|
85
|
+
*/
|
|
86
|
+
export function unregisterFont(name) {
|
|
87
|
+
delete FONTS[name];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Clear all registered fonts (mainly for testing)
|
|
92
|
+
*/
|
|
93
|
+
export function clearFonts() {
|
|
94
|
+
Object.keys(FONTS).forEach(key => delete FONTS[key]);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Re-export compression utilities for font authors
|
|
98
|
+
export { codeList, decodeList };
|
package/src/globals.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global type declarations for MeshWriter UMD builds
|
|
3
|
+
* Include this file if using MeshWriter via script tag
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { MeshWriterStatic } from './types';
|
|
7
|
+
|
|
8
|
+
declare global {
|
|
9
|
+
/** Global MeshWriter (UMD bundle) */
|
|
10
|
+
var MeshWriter: MeshWriterStatic | undefined;
|
|
11
|
+
/** Legacy name for MeshWriter (UMD bundle) */
|
|
12
|
+
var TYPE: MeshWriterStatic | undefined;
|
|
13
|
+
|
|
14
|
+
namespace BABYLON {
|
|
15
|
+
/** MeshWriter attached to BABYLON namespace (UMD bundle) */
|
|
16
|
+
var MeshWriter: MeshWriterStatic | undefined;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export {};
|