three.js: Editor - broken LDRAW support and missing PDB support
Description
Using online editor to load LDRAW file ends in error (both Firefox and Chrome browsers on Windows).
The 1st code below is actually what works for loading LDRAW files, with additional .DAT and .L3B formats + using @gkjohnson repository + removed undefined parameter from the loader.parse function call.
The 2nd code shows what works if PDB format is to be considered for inclusion, which is mainly the code extracted from the webgl_loader_pdb.html example.
I can submit a PR if this code is acceptable.
Reproduction steps
- Use either Firefox or Chrome browser and go to
https://threejs.org/editor/ - Try to load any LDRAW file
Code
case 'dat':
case 'l3b':
case 'ldr':
case 'mpd':
{
reader.addEventListener( 'load', async function ( event ) {
const { LDrawLoader } = await import( 'three/addons/loaders/LDrawLoader.js' );
const loader = new LDrawLoader();
// The path to preload color definitions from.
await loader.preloadMaterials( 'https://raw.githubusercontent.com/gkjohnson/ldraw-parts-library/master/colors/ldcfgalt.ldr' );
// The path to load parts from the LDraw parts library from.
loader.setPartsLibraryPath( 'https://raw.githubusercontent.com/gkjohnson/ldraw-parts-library/master/complete/ldraw/' );
loader.setPath( '../../examples/models/ldraw/officialLibrary/' );
loader.parse( event.target.result, function ( group ) {
group.name = filename;
// Convert from LDraw coordinates: rotate 180 degrees around OX
group.rotation.x = Math.PI;
// Scale and add model groups to the scene
group.scale.multiplyScalar( 0.1 );
editor.execute( new AddObjectCommand( editor, group ) );
} );
}, false );
reader.readAsText( file );
break;
}
case 'pdb':
{
reader.addEventListener( 'load', async function ( event ) {
const atomGeometry = new THREE.IcosahedronGeometry( 1, 2 );
const bondGeometry = new THREE.BoxGeometry( 1, 1, 1 );
let position = new THREE.Vector3();
let offset = new THREE.Vector3();
let start = new THREE.Vector3();
let end = new THREE.Vector3();
let color = new THREE.Color();
let root = new THREE.Group();
root.name = filename;
let atoms = [];
let bonds = [];
const contents = event.target.result;
const { PDBLoader } = await import( '../../examples/jsm/loaders/PDBLoader.js' );
let pdb = new PDBLoader().parse( contents );
// Get atom related data
let geometryAtoms = pdb.geometryAtoms;
// Get the data of the bond between atoms
let geometryBonds = pdb.geometryBonds;
// Get data of atomic text
let json = pdb.json;
// Center the model
geometryAtoms.computeBoundingBox();
geometryAtoms.boundingBox.getCenter( offset ).negate();
geometryAtoms.translate( offset.x, offset.y, offset.z );
geometryBonds.translate( offset.x, offset.y, offset.z );
// Add atoms to the model group
let positions = geometryAtoms.getAttribute( 'position' );
let colors = geometryAtoms.getAttribute( 'color' );
for ( var i = 0; i < positions.count; i ++ ) {
position.x = positions.getX( i );
position.y = positions.getY( i );
position.z = positions.getZ( i );
color.r = colors.getX( i );
color.g = colors.getY( i );
color.b = colors.getZ( i );
let object = new THREE.Mesh( atomGeometry, new THREE.MeshStandardMaterial( { color: color } ) );
object[ 'name' ] = 'atom_' + i;
object.position.copy( position );
object.position.multiplyScalar( 75 );
object.scale.multiplyScalar( 25 );
// Add atomic text to the atom's userData
let atom = json.atoms[ i ];
object.userData[ 'Element' ] = atom[ 4 ];
atoms.push( object );
root.add( object );
}
// Add bonds between atoms to the model group
positions = geometryBonds.getAttribute( 'position' );
for ( var i = 0; i < positions.count; i += 2 ) {
start.x = positions.getX( i );
start.y = positions.getY( i );
start.z = positions.getZ( i );
end.x = positions.getX( i + 1 );
end.y = positions.getY( i + 1 );
end.z = positions.getZ( i + 1 );
start.multiplyScalar( 75 );
end.multiplyScalar( 75 );
let object = new THREE.Mesh( bondGeometry, new THREE.MeshStandardMaterial( { color: 0xFFFFFF } ) );
object[ 'name' ] = 'bond_' + parseInt( i / 2 );
object.position.copy( start );
object.position.lerp( end, 0.5 );
object.scale.set( 5, 5, start.distanceTo( end ) );
object.lookAt( end );
bonds.push( object );
root.add( object );
}
// Scale and add model groups to the scene
root.scale.multiplyScalar( 0.05 );
editor.execute( new AddObjectCommand( editor, root ) );
}, false );
reader.readAsText( file );
break;
}
Live example
Screenshots
No response
Version
r153
Device
Desktop
Browser
Chrome, Firefox
OS
Windows
About this issue
- Original URL
- State: closed
- Created a year ago
- Comments: 23 (2 by maintainers)
I’ll do a PR with the change.
Ok, I’ll do the wipe thing at the end of parse in some days.
All right then, go ahead.
I’ll tried it first however it didn’t completely solve the issue. I still got runtime errors during serialization.
Yeah they’ll be kept around because you’ve assigned them to other local variables.
It was easy. I’ve made it by cloning the material (before cloning I unhang temporarily the non-serializable fields, there are only 2 of them) This has the benefit of not touching the materials in the loader library, so the loader is not affected and can be used to parse more files.
In
LDrawLoader.applyMaterialsToMesh(), inside the localgetMaterial()function, at the end, I’ve changedreturn material;by: (edited)cc @gkjohnson Do you see it okay?
Okay, thanks for the explanation!
Storing things in
userDatais of course fine as long as it does not break serialization/deserialization. But if the contents ofuserDatacreated byLDRAWLoaderhave no relevance for the app, it’s indeed best to clean things up.A model material related to a mesh has associated the material for the edge lines of the same object. This one in turn can also have associated a conditional edges material (edges with a sort of back-face auto-culling)
This is only needed for building the model while parsing so perhaps the second solution I proposed is the best one (I can give it a try)
I did put them in
userDatabecause at the time the use of this field was more common. InSVGLoaderfor example the path style is also in the pathuserData.If the class
ConditionalLineSegmentsis not serializable itself, then I think the conditional segments can’t be saved but I’m not sure about this.If I am not wrong, without using @gkjohnson parts repository will have the editor be limited to loading only pre-packed
mpdfiles. This is quick and efficient since loading parts from the repo does produce lots of traffic.I only created this issue so it could be looked at. You guys can handle it and close this at any time.