Sorry @malcolm just now getting a chance to come back to this - So I did some more research, and came across assimp (open asset import library) and also came across a subsequent python library/bindings for it called impasse.
With some work, I was able to get the dll compiled and the python library working in TD.
The script SOP loads the obj with the below code:
import impasse
import numpy
def onCook(scriptOp):
scriptOp.clear()
scriptOp.vertexAttribs.create('N')
scriptOp.vertexAttribs.create('T')
scriptOp.vertexAttribs.create('B',(0.0,0.0,0.0))
scriptOp.vertexAttribs.create('uv')
scriptOp.vertexAttribs.create('Cd')
file_path = 'C:/ASSIMP/bin/Release/mesh.obj'
scene = impasse.load(file_path)
# https://github.com/SaladDais/Impasse/blob/master/impasse/constants.py
import_actions = (
0
| impasse.constants.ProcessingStep.CalcTangentSpace
| impasse.constants.ProcessingStep.JoinIdenticalVertices
# | impasse.constants.ProcessingStep.MakeLeftHanded
| impasse.constants.ProcessingStep.Triangulate
# | impasse.constants.ProcessingStep.RemoveComponent
# | impasse.constants.ProcessingStep.GenNormals
# | impasse.constants.ProcessingStep.GenSmoothNormals
# | impasse.constants.ProcessingStep.SplitLargeMeshes
# | impasse.constants.ProcessingStep.PreTransformVertices
# | impasse.constants.ProcessingStep.LimitBoneWeights
# | impasse.constants.ProcessingStep.ValidateDataStructure
| impasse.constants.ProcessingStep.ImproveCacheLocality
# | impasse.constants.ProcessingStep.RemoveRedundantMaterials
# | impasse.constants.ProcessingStep.FixInfacingNormals
# | impasse.constants.ProcessingStep.SortByPType
# | impasse.constants.ProcessingStep.FindDegenerates
# | impasse.constants.ProcessingStep.FindInvalidData
| impasse.constants.ProcessingStep.GenUVCoords
# | impasse.constants.ProcessingStep.TransformUVCoords
# | impasse.constants.ProcessingStep.FindInstances
| impasse.constants.ProcessingStep.OptimizeMeshes
| impasse.constants.ProcessingStep.OptimizeGraph
# | impasse.constants.ProcessingStep.FlipUVs
# | impasse.constants.ProcessingStep.FlipWindingOrder
# | impasse.constants.ProcessingStep.SplitByBoneCount
# | impasse.constants.ProcessingStep.Debone
# | impasse.constants.ProcessingStep.GenEntityMeshes
# | impasse.constants.ProcessingStep.OptimizeAnimations
# | impasse.constants.ProcessingStep.FixTexturePaths
# | impasse.constants.ProcessingStep.EmbedTextures
)
scene = impasse.load(file_path, processing=import_actions )
### get some data from mesh while converting numpy lists to regular lists.
name = scene.meshes[0].name
primitive_types = scene.meshes[0].primitive_types
num_uv_components = scene.meshes[0].num_uv_components
anim_meshes = [ list(item) for item in scene.meshes[0].anim_meshes ]
faces = [ list(item) for item in scene.meshes[0].faces ]
vertices = [ list(item) for item in scene.meshes[0].vertices ]
texture_coords = [ list(item) for item in scene.meshes[0].texture_coords[0] ]
normals = [ list(item) for item in scene.meshes[0].normals ]
tangents = [ list(item) for item in scene.meshes[0].tangents ]
bitangents = [ list(item) for item in scene.meshes[0].bitangents ]
colors = [ list(item) for item in scene.meshes[0].colors ]
bones = [ list(item) for item in scene.meshes[0].bones ]
for i,vtx in enumerate(vertices):
p = scriptOp.appendPoint()
p.x , p.y , p.z = vtx
for face in faces:
f = scriptOp.appendPoly(len(face), closed=True, addPoints=False)
for vtx in f:
i = face[vtx.index]
vtx.point = scriptOp.points[i]
if len(normals):
vtx.N = normals[i]
if len(tangents):
vtx.T = tangents[i]+[1] ### NOTE this is technically wrong, we should not be setting this to 1 hardcoded, if winding order is reversed, it'd be -1..
vtx.B = bitangents[i] ### calculating this in shader.. but we could eventually pass it in via sop if it's faster..
if len(texture_coords):
vtx.uv = texture_coords[i]
if len(colors):
vtx.Cd = colors[i]
return
The result of this is a 3d mesh with tangent/bitangent vectors averaged for vertices that share the same point.
This produces smoothly interpolated tangents and bitangents similar to how normals are smoothly interpolated when vertices share the same point.
You can see for the tangents and bitangents, you get these noticeable seams across faces when the vectors are not aligned/averaged for shared points, which is the root cause of the artifacts I posted about earlier.
I know this is totally nit picking for the current version of TD’s pbr shading model as the artifact does not manifest it’s self in any meaningful way, but it will certainly be a problem for anisotropic material models of the future, and also it just feels like something that should behave similar to normals, in how they are interpreted for smooth vs faceted surfaces.
Also, I do believe Substance Painter/Designer etc use this in their backend for importing 3d models, so since we are also trying to have parity with substance sbars, and all that, it might make sense to interpret/import/export the meshes the same way they do as well, to ensure sbar’s and materials look as closely matched between ecosystems. I haven’t seen anywhere substance say they use it, I’ve only seen it mentioned sparsely in logs and errors occasionally, fairly sure it’s in use though.
Happy to package up this assimp module/python stuffs for further investigation if needed.
Also, has Derivative considered using assimp? They support a huge family of 3d file formats, and have some other cool features as well (see flags in above code)
COMMON INTERCHANGE FORMATS
Autodesk ( .fbx )
Collada ( .dae )
glTF ( .gltf, .glb )
Blender 3D ( .blend )
3ds Max 3DS ( .3ds )
3ds Max ASE ( .ase )
Wavefront Object ( .obj )
Industry Foundation Classes (IFC/Step) ( .ifc )
XGL ( .xgl,.zgl )
Stanford Polygon Library ( .ply )
*AutoCAD DXF ( .dxf )
LightWave ( .lwo )
LightWave Scene ( .lws )
Modo ( .lxo )
Stereolithography ( .stl )
DirectX X ( .x )
AC3D ( .ac )
Milkshape 3D ( .ms3d )
* TrueSpace ( .cob,.scn )
MOTION CAPTURE FORMATS
Biovision BVH ( .bvh )
* CharacterStudio Motion ( .csm )
GRAPHICS ENGINE FORMATS
Ogre XML ( .xml )
Irrlicht Mesh ( .irrmesh )
* Irrlicht Scene ( .irr )
GAME FILE FORMATS
Quake I ( .mdl )
Quake II ( .md2 )
Quake III Mesh ( .md3 )
Quake III Map/BSP ( .pk3 )
* Return to Castle Wolfenstein ( .mdc )
Doom 3 ( .md5* )
*Valve Model ( .smd,.vta )
*Open Game Engine Exchange ( .ogex )
*Unreal ( .3d )
OTHER FILE FORMATS
BlitzBasic 3D ( .b3d )
Quick3D ( .q3d,.q3s )
Neutral File Format ( .nff )
Sense8 WorldToolKit ( .nff )
Object File Format ( .off )
PovRAY Raw ( .raw )
Terragen Terrain ( .ter )
3D GameStudio (3DGS) ( .mdl )
3D GameStudio (3DGS) Terrain ( .hmp )
Izware Nendo ( .ndo )