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

Refactor for V1 #56

Open
EliCDavis opened this issue Jan 26, 2025 · 0 comments
Open

Refactor for V1 #56

EliCDavis opened this issue Jan 26, 2025 · 0 comments

Comments

@EliCDavis
Copy link
Owner

EliCDavis commented Jan 26, 2025

This issue description is a working document of my thoughts on what needs to be changed before V1. It will change overtime as I find new issues and my opinions of things evolve.

Preface

Honestly, I haven't been happy with the OBJ package API (and most formats) for a while. OBJ was the first format I implemented in polyform years ago, and it's probably the ugliest to work with. Being OBJ was the first format, it also has had the largest influence on the material API in polyform, which is also not amazing to work with.

I've kept polyform in v0 for years and I want to start to approach a v1. I think the format and material APIs are some sore thumbs that need looking at before v1 is here.

Problems with Materials

It's implemented such that it takes up ranges of topology primitives. IE: The first 9 triangles in a mesh have one material, the next 6 could have another, and then the next 12 could have no material. The only format implemented that even supports such a feature is OBJ, and it makes certain mesh operations not obvious on how to properly implement (IE what happens in a "merge vertices by distance" when different vertices have different materials)
The fields in a polyform material are basically a carbon copy of OBJ's, which doesn't serve to support any other format other than some of what's in GLTF.
Polyform materials are implemented as pointers to support nullability. This allows developers to break the immutability aspect of a mesh by allowing a programmer to keep a reference to a material and make changes to it without directly messing with the mesh, which goes against much of what polyform is trying to prevent.

Solution

Delete the concept of materials right out of the Mesh, and defer implementation to the format in question. So now you'll be dealing with a obj.Material directly.

Problem with Format API

Currently, the API has no cohesion from format to format. A bunch of common terms are used, Save, Load, Write, and Read, but the signatures themselves change from format to format. Maybe I'm making this a bigger problem in my head than it actually is. But it'd be nice if there was some cohesion, ideally some semblance of a common interface.

Below is an overview of the current state of affairs;

// PLY ===========================================================================
ply.Save(plyPath string, meshToSave modeling.Mesh, format ply.Format) error
ply.Write(out io.Writer, model modeling.Mesh, format ply.Format) error
ply.Load(filepath string) (*modeling.Mesh, error)
ply.ReadMesh(in io.Reader) (*modeling.Mesh, error)

// GLTF ==========================================================================
gltf.Save(modelPath string, scene gltf.PolyformScene) error
gltf.SaveBinary(gltfPath string, scene gltf.PolyformScene) error
gltf.SaveText(gltfPath string, scene gltf.PolyformScene) error
gltf.WriteBinary(scene gltf.PolyformScene, out io.Writer) error
gltf.WriteText(scene gltf.PolyformScene, out io.Writer) error

// OBJ ===========================================================================
obj.Save(objPath string, meshToSave modeling.Mesh) error
obj.WriteMesh(m modeling.Mesh, materialFile string, out io.Writer) error
obj.WriteMeshes(meshes []obj.ObjMesh, materialFile string, out io.Writer) error
obj.WriteMaterial(mat modeling.Material, out io.Writer) (err error)
obj.WriteMaterials(m modeling.Mesh, out io.Writer) error
obj.Load(objPath string) ([]obj.ObjMesh, error)
obj.ReadMaterials(in io.Reader) ([]modeling.Material, error)
obj.ReadMesh(in io.Reader) ([]obj.ObjMesh, []string, error)

// STL ===========================================================================
stl.Save(fp string, m modeling.Mesh) error
stl.Write(out io.Writer, bin stl.Binary) error
stl.WriteMesh(out io.Writer, m modeling.Mesh) error
stl.Load(fp string) (*modeling.Mesh, error)
stl.ReadMesh(in io.Reader) (*modeling.Mesh, error)

Solution

Generally, formats have extra data associated with them that don't fit into a mesh. STL has an 80 byte header, OBJ has materials, GLTF has a ton of stuff, and PLY has comments and obj_info. This means that no matter what, signatures are bound to be different in some capacity.

To allow developers to ignore the format details, we supply an API serving as our "best guess" for translating data into a single polyform mesh. This "best guess" will inevitably fall short for use cases that require context around the mesh that the format in question provides, and in those scenarios, the developer will then need to use the format-specific APIs to resolve those features.

The general purpose interface that every format needs to implement is as follows:

type Format {
    ReadMesh(io.Reader)  (modeling.Mesh, error)
    WriteMesh(io.Writer, modeling.Mesh) error 
    LoadMesh(path string) (modeling.Mesh, error)
    SaveMesh(path string, modeling.Mesh) error
}

We then reserve the function names Read, Write, Load, and Save to have their signatures dependent on the details of the format in question. So the Load in the obj package will look different to the Load in the stl package.

Problem with Producers/Artifacts

Right now, producers create artifacts. Artifacts themselves define their mime type, which means for a given producer, you won't know what mime type it produces until runtime. This needs to be shifted to the producer and somehow enforced so that the artifact adheres to the mime type. I'm not sure of the best way to implement this

There's also no clear solution as to how a artifact might want to produce multiple files. Some examples

  • GLTF + sidcare bin
  • 3D Tiles
  • OBJ + MTL file

Problem with Struct Nodes

There are a few problems with Struct nodes

  • They can only have one output ("Out")
  • They're kinda gross to define
  • You can't connect nodes with mismatch types despite it being technically correct
    • IE. IF my input port is an interface type Shape, I can't connect a node of type Quad, even though Quad implements Shape
  • No generic support
    • I can't create any generic functionality like "getting length of an array". I'd have to create a node specific to the type of array I want to get the length of (ie LengthOfShapeArrayNode)
  • Input/Output array
    • For stuff like math, it'd be nice to be able for nodes to optionally accept either single values or arrays of values, and optionally put out single values or arrays of values
      • A Sin node should easily do either of these things.

Problems with GLTF format

The implementation of the GLTF format is incomplete. We still need to satisfy

  • Animation
    • Basic
    • Things
  • Embedded images
  • Reference binaries for GLTF + GLB
  • Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant