diff --git a/src/utils/parser.js b/src/utils/parser.js index a2a15c2..def30b9 100644 --- a/src/utils/parser.js +++ b/src/utils/parser.js @@ -13,9 +13,23 @@ function parse(gltf, { fileName = 'model', ...options } = {}) { const animations = gltf.animations const hasAnimations = animations.length > 0 + /** @type {Record */ + const slots = {} + // Collect all objects const objects = [] - gltf.scene.traverse((child) => objects.push(child)) + gltf.scene.traverse((child) => { + objects.push(child); + + // Collect slots + const slot = child.userData?.prop; + const hasSlot = (slot && typeof slot === "string" && slot.length > 0); + if (hasSlot) + { + const slotname = sanitizeSlotName(slot); + slots[slotname] ? slots[slotname].push(child) : (slots[slotname] = [child]); + } + }) // Browse for duplicates const duplicates = { @@ -75,6 +89,11 @@ function parse(gltf, { fileName = 'model', ...options } = {}) { return isVarName(name) ? `.${name}` : `['${name}']` } + /** Ensure that a slot is a valid variable name e.g. must not contain spaces */ + function sanitizeSlotName(slotname) { + return slotname.replaceAll(/[^a-zA-Z0-9]/g, ''); + } + const rNbr = (number) => { return parseFloat(number.toFixed(Math.round(options.precision || 2))) } @@ -219,7 +238,8 @@ function parse(gltf, { fileName = 'model', ...options } = {}) { duplicates.geometries[obj.geometry.uuid + obj.material.name] && duplicates.geometries[obj.geometry.uuid + obj.material.name].count > (options.instanceall ? 0 : 1) let animated = gltf.animations && gltf.animations.length > 0 - return { type, node, instanced, animated } + const hasSlots = obj.userData?.prop && typeof obj.userData.prop === "string" && obj.userData.prop.length > 0; + return { type, node, instanced, animated, hasSlots } } function equalOrNegated(a, b) { @@ -227,9 +247,9 @@ function parse(gltf, { fileName = 'model', ...options } = {}) { } function prune(obj, children, result, oldResult, silent) { - let { type, animated } = getInfo(obj) + let { type, animated, hasSlots } = getInfo(obj) // Prune ... - if (!obj.__removed && !options.keepgroups && !animated && (type === 'group' || type === 'scene')) { + if (!obj.__removed && !options.keepgroups && !animated && !hasSlots && (type === 'group' || type === 'scene')) { /** Empty or no-property groups * * @@ -370,13 +390,24 @@ function parse(gltf, { fileName = 'model', ...options } = {}) { // Bail out if the object was pruned if (pruned !== undefined) return pruned - // Close tag - result += `${children.length ? '>' : '/>'}\n` + // Add custom slots if defined in the object's userData + // E.g. userData: { "prop" : "mySlot" } becomes `{ mySlot }` + const slot = obj.userData?.prop; + const hasSlot = (slot && typeof slot === "string" && slot.length > 0); + const hasContent = children.length || hasSlot; - // Add children and return - if (children.length) { - if (type === 'bone') result += children + `` - else result += children + `` + + // Close tag if no children + result += `${hasContent ? '>' : '/>'}\n` + + // Add children + if (children.length) result += `${children.trimEnd("\n")}\n` + // Add custom slot + if (hasSlot) result += `{${sanitizeSlotName(slot)}}\n`; + // Close tag + if (hasContent) { + if (type === 'bone') result += `` + else result += `` } return result } @@ -440,10 +471,14 @@ function parse(gltf, { fileName = 'model', ...options } = {}) { } catch (e) { console.log('Error while parsing glTF', e) } + + const slotParams = Object.keys(slots).length > 0 ? (Object.keys(slots).join(", ") + ", ") : ""; + const header = `/* ${options.header ? options.header : 'Auto-generated by: https://github.com/pmndrs/gltfjsx'} ${ options.size ? `\nFiles: ${options.size}` : '' } + ${parseExtras(gltf.parser.json.asset && gltf.parser.json.asset.extras)}*/` const hasPrimitives = scene.includes('