Skip to content

Commit

Permalink
add support for generating slots from blender properties
Browse files Browse the repository at this point in the history
  • Loading branch information
marwie committed Jul 13, 2024
1 parent 7cb8e67 commit 6d727db
Showing 1 changed file with 47 additions and 12 deletions.
59 changes: 47 additions & 12 deletions src/utils/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,23 @@ function parse(gltf, { fileName = 'model', ...options } = {}) {
const animations = gltf.animations
const hasAnimations = animations.length > 0

/** @type {Record<string, Object3D[]> */
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 = {
Expand Down Expand Up @@ -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)))
}
Expand Down Expand Up @@ -219,17 +238,18 @@ 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) {
return (a.x === b.x || a.x === -b.x) && (a.y === b.y || a.y === -b.y) && (a.z === b.z || a.z === -b.z)
}

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
* <group>
* <mesh geometry={nodes.foo} material={materials.bar} />
Expand Down Expand Up @@ -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 + `</primitive>`
else result += children + `</${type}>`

// 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 += `</primitive>`
else result += `</${type}>`
}
return result
}
Expand Down Expand Up @@ -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('<primitive')
const result = `${options.types ? `\nimport * as THREE from 'three'` : ''}
Expand All @@ -465,7 +500,7 @@ ${parseExtras(gltf.parser.json.asset && gltf.parser.json.asset.extras)}*/`
? `
const context = React.createContext(${options.types ? '{} as ContextType' : ''})
export function Instances({ children, ...props }${options.types ? ': JSX.IntrinsicElements["group"]' : ''}) {
export function Instances({ children, ${slotParams}...props }${options.types ? ': JSX.IntrinsicElements["group"]' : ''}) {
const { nodes } = useGLTF('${url}'${options.draco ? `, ${JSON.stringify(options.draco)}` : ''})${
options.types ? ' as GLTFResult' : ''
}
Expand All @@ -486,7 +521,7 @@ ${parseExtras(gltf.parser.json.asset && gltf.parser.json.asset.extras)}*/`
: ''
}
export ${options.exportdefault ? 'default' : ''} function Model(props${
export ${options.exportdefault ? 'default' : ''} function Model({ ${slotParams}...props }${
options.types ? ": JSX.IntrinsicElements['group']" : ''
}) {
${hasInstances ? 'const instances = React.useContext(context);' : ''} ${
Expand Down

0 comments on commit 6d727db

Please sign in to comment.