Skip to content

Conversation

rosskevin
Copy link

@rosskevin rosskevin commented Jan 31, 2025

Temporary fork published

Temporary fork published: https://github.com/rosskevin/gltfjsx

I hope this is a temporary package and this PR is accepted, but I've got to move on and start using it in a production/published environment.

Goal

  • Allow for external reuse via API (load, transform, analyze, generate)
  • Allow for customization of analyze/duplicates/pruning
  • Allow external users to generate non-r3f components through direct api access
  • Allow for customization of generated code

Included

  • Added eslint
  • Removed rollup for simpler tsup config
  • Converted to typescript
  • Updated all dependencies
  • Integrated auto for automatic releases (NEED a committer to work with me or commit access to complete setup)
  • Separate generate code from analyze, simplifying and reducing some duplicate logic with better maintainability
  • Allow pluggable pruning strategies for the analyze phase
  • Allow subclassing for analyze and generate phases.
  • Integrated ts-morph for AST access or string building, whichever is easiest. API consumer can direct access and modify the AST. Allows for easy tsx | jsx stringification.
  • Removed copied loaders src and instead use community maintained node-three-gltf
  • Add tests (just the start, but it's a start!)
  • Resolution is nodenext, so this is an ESM build with top-level await for node 16+
  • Optimized npm package via ignores
  • Better naming in generated code for typescript types.
  • Fix for duplicate named nodes in the typescript interface
  • new flatten optimation exposed that defaults to true (existing behavior) but can be turned off via API or CLI (this can unexpectedly remove named node references)
  • new exposeProps option in generation API, allowing for generating component props that propagate to Object3D one to many jsx properties e.g. shadows -> [castShadow, receiveShadow] with optional matcher fn.

Motivation

We have three CAD models that have continuous development. We are building online configurators, and we effectively need a way to customize gltfjsx to instrument the code with our additions in a continuous integration way.

Testing

  • I have written basic tests, previously there were none. Additional assertions have been added to support new features like exposeProps.
  • I have performed some comparison generations via CLI that look good.
  • I am using the code published in the fork as an API (for customized/advanced options)

TL;DR

I need this, and I spent a lot of time on it. I'll be moving forward with it regardless, but I think it belongs here. I hope to have this PR reconciled and merged so that the entire community can collaborate.

Samples

GLTF r3f
/*
  FOO header 
*/

import {
  useAnimations,
  useGLTF,
  Merged,
  PerspectiveCamera,
  OrthographicCamera,
} from '@react-three/drei'
import { GroupProps, MeshProps, useGraph } from '@react-three/fiber'
import * as React from 'react'
import { AnimationClip, Mesh, MeshPhysicalMaterial, MeshStandardMaterial } from 'three'
import { GLTF, SkeletonUtils } from 'three-stdlib'

interface FlightHelmetGLTF extends GLTF {
  nodes: {
    Hose_low: Mesh
    RubberWood_low: Mesh
    GlassPlastic_low: Mesh
    MetalParts_low: Mesh
    LeatherParts_low: Mesh
    Lenses_low: Mesh
  }
  materials: {
    HoseMat: MeshStandardMaterial
    RubberWoodMat: MeshStandardMaterial
    GlassPlasticMat: MeshStandardMaterial
    MetalPartsMat: MeshStandardMaterial
    LeatherPartsMat: MeshStandardMaterial
    LensesMat: MeshPhysicalMaterial
  }
}

export interface FlightHelmetProps extends GroupProps {}

const modelLoadPath = '/FlightHelmet.gltf'
const draco = false

export function FlightHelmet(props: FlightHelmetProps) {
  const { nodes, materials } = useGLTF(modelLoadPath, draco) as FlightHelmetGLTF

  return (
    <group {...props} dispose={null}>
      <mesh
        name="Hose_low"
        castShadow
        receiveShadow
        geometry={nodes.Hose_low.geometry}
        material={materials.HoseMat}
      />
      <mesh
        name="RubberWood_low"
        castShadow
        receiveShadow
        geometry={nodes.RubberWood_low.geometry}
        material={materials.RubberWoodMat}
      />
      <mesh
        name="GlassPlastic_low"
        castShadow
        receiveShadow
        geometry={nodes.GlassPlastic_low.geometry}
        material={materials.GlassPlasticMat}
      />
      <mesh
        name="MetalParts_low"
        castShadow
        receiveShadow
        geometry={nodes.MetalParts_low.geometry}
        material={materials.MetalPartsMat}
      />
      <mesh
        name="LeatherParts_low"
        castShadow
        receiveShadow
        geometry={nodes.LeatherParts_low.geometry}
        material={materials.LeatherPartsMat}
      />
      <mesh
        name="Lenses_low"
        castShadow
        receiveShadow
        geometry={nodes.Lenses_low.geometry}
        material={materials.LensesMat}
      />
    </group>
  )
}

useGLTF.preload(modelLoadPath, draco)
Draco instanceall r3f
/*
  FOO header 
*/

import {
  useAnimations,
  useGLTF,
  Merged,
  PerspectiveCamera,
  OrthographicCamera,
} from '@react-three/drei'
import { GroupProps, MeshProps, useGraph } from '@react-three/fiber'
import * as React from 'react'
import { AnimationClip, Mesh, MeshPhysicalMaterial, MeshStandardMaterial } from 'three'
import { GLTF, SkeletonUtils } from 'three-stdlib'

interface FlightHelmetGLTF extends GLTF {
  nodes: {
    GlassPlastic_low: Mesh
    Hose_low: Mesh
    Lenses_low: Mesh
    RubberWood_low: Mesh
    MetalParts_low: Mesh
    LeatherParts_low: Mesh
  }
  materials: {
    GlassPlasticMat: MeshStandardMaterial
    HoseMat: MeshStandardMaterial
    LensesMat: MeshPhysicalMaterial
    RubberWoodMat: MeshStandardMaterial
    MetalPartsMat: MeshStandardMaterial
    LeatherPartsMat: MeshStandardMaterial
  }
}

export interface FlightHelmetProps extends GroupProps {}

const modelLoadPath = '/FlightHelmet-transformed.glb'
const draco = true

type ContextType = Record<string, React.ForwardRefExoticComponent<MeshProps>>

const context = React.createContext<ContextType>({})

export function FlightHelmetInstances({ children, ...props }: FlightHelmetProps) {
  const { nodes } = useGLTF(modelLoadPath, draco) as FlightHelmetGLTF
  const instances = React.useMemo(
    () => ({
      GlassPlastic_low: nodes.GlassPlastic_low,
      Hose_low: nodes.Hose_low,
      Lenses_low: nodes.Lenses_low,
      RubberWood_low: nodes.RubberWood_low,
      MetalParts_low: nodes.MetalParts_low,
      LeatherParts_low: nodes.LeatherParts_low,
    }),
    [nodes],
  )
  return (
    <Merged meshes={instances} {...props}>
      {(instances: ContextType) => <context.Provider value={instances} children={children} />}
    </Merged>
  )
}

export function FlightHelmet(props: FlightHelmetProps) {
  const instances = React.useContext(context)

  return (
    <group {...props} dispose={null}>
      <instances.GlassPlastic_low name="GlassPlastic_low" />
      <instances.Hose_low name="Hose_low" />
      <instances.Lenses_low name="Lenses_low" />
      <instances.RubberWood_low name="RubberWood_low" />
      <instances.MetalParts_low name="MetalParts_low" />
      <instances.LeatherParts_low name="LeatherParts_low" />
    </group>
  )
}

useGLTF.preload(modelLoadPath, draco)

Closes/supercedes PRs

This closes the majority of open PRs through fixes or through reuse of external loaders that are maintained:

Closes issues

This PR allows-for/solves/obsoletes/no longer present the following issues:

clicktodev and others added 30 commits September 11, 2024 01:44
…mpty leaf nodes

The code changes introduce two new options to the CLI tool:
- `--pruneKeepAttributes`: Determines whether to keep unused vertex attributes, such as UVs without an assigned texture.
- `--pruneKeepLeaves`: Determines whether to keep empty leaf nodes.

These options provide more control over the pruning process, allowing users to optimize the resulting glTF files based on their specific requirements.
…e I think we can import these, so try that before continuing on these
@rosskevin
Copy link
Author

For watchers, I updated the original text, but:

Temporary fork published: https://github.com/rosskevin/gltfjsx @rosskevin/gltfjsx

I hope this is a temporary package and #286 is accepted, but I've got to move on and start using it in a production/published environment.

@donmccurdy
Copy link
Member

Hi @rosskevin! Awesome work here, thank you! I'm in favor of the direction. But I'm only an occasional contributor on this repository, and so I think we'll need @drcmda to sign off on the plan. It is a pretty large PR, so I'll wait to do a deeper review until getting some signal one way or the other.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment