Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for generating slots from blender custom properties #265

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -221,17 +240,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 @@ -379,13 +399,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 @@ -449,10 +480,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 @@ -474,7 +509,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 @@ -495,7 +530,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
Loading