Closed mkhorton closed 2 years ago
Commenting here in case anyone else finds this thread and is looking for something similar:
https://github.com/jzitelli/three.py exists and seems to work, though it hasn't been updated in a while -- not sure if this is the older three.js JSON format, looks similar to the current format though.
If we assume that it is executed in a notebook during scene creation, we can simply use three's own toJSON()
function, and save the output. There are two possible ways to do this:
To achieve 1., we would need to add a button or message that triggers the toJSON()
function on the relevant three object (the scene in this case). See e.g. how the Three editor does it.
For 2., this is currently possible, just not very elegant:
import json
def export_scene(scene, filename):
old_ret_val = scene._on_ret_val
def on_data(method, data):
if method != 'toJSON':
return
scene._on_ret_val = old_ret_val
json.dump(data, filename)
scene._on_ret_val = on_data
scene.exec_three_obj_method('toJSON')
The logic behind 2. could be cleaned up if there was an signal/slot event logic to the execute method (i.e. no overwriting/monkey-patching). That would be a pure python thing, so anyone proficient enough in Python could contribute it.
If we assume that it is executed in a notebook during scene creation.
If this assumption doesn't hold, we could try to have a pure Python implementation based on the widgets / autogen code, and current serializers. I'm not entirely sure how much work this would be, either to develop or to maintain.
Hi @vidartf , thanks for the reply ! I didn't realize the toJSON()
function was available, so that's good to know.
Ideally, it'd be executed outside the notebook -- I'd like to generate of the order of 100k scene JSONs so a pure Python approach would be great, though I imagine I can write a script to do this via a notebook. I'll try the method 2 you suggested to start with and see how that works.
If you want something pure Python, you could try something like this (might not be a perfect fit, but it would be interesting to see where it differs, and by how much):
import json
from ipywidgets.embed import dependency_state
def export_scene(scene, filename):
state = dependency_state(scene)
json.dump(state, filename)
Hmm, this is interesting. It definitely seems like the output from the dependency_state
could be converted into the JSON format.
As an example,
ball = Mesh(geometry=SphereGeometry(radius=1),
material=LambertMaterial(color='red'),
position=[2, 1, 0])
c = PerspectiveCamera(position=[0, 5, 5], up=[0, 1, 0],
children=[DirectionalLight(color='white', position=[3, 5, 1], intensity=0.5)])
scene = Scene(children=[ball, c, AmbientLight(color='#777777')])
gives:
{'10b0a2c543954ec99628b3ce0bcd84f7': {'model_module': 'jupyter-threejs',
'model_module_version': '0.3.0-alpha.0',
'model_name': 'MeshModel',
'state': {'children': [],
'geometry': 'IPY_MODEL_c7d34028e78a4d2dacbfad3eb7ebb752',
'material': 'IPY_MODEL_4654ccdfb8bb49049746c729235fcb94',
'position': [2.0, 1.0, 0.0],
'quaternion': [],
'scale': [1.0, 1.0, 1.0],
'up': [0.0, 1.0, 0.0]}},
'402cf0b394cb45389981f72c82e05343': {'model_module': 'jupyter-threejs',
'model_module_version': '0.3.0-alpha.0',
'model_name': 'PerspectiveCameraModel',
'state': {'children': ['IPY_MODEL_9cd3e202ba01477cb51db5d14e66d005'],
'position': [0.0, 5.0, 5.0],
'quaternion': [],
'scale': [1.0, 1.0, 1.0],
'up': [0.0, 1.0, 0.0]}},
'4654ccdfb8bb49049746c729235fcb94': {'model_module': 'jupyter-threejs',
'model_module_version': '0.3.0-alpha.0',
'model_name': 'LambertMaterialModel',
'state': {'color': 'red',
'envMap': None,
'lightMap': None,
'map': None,
'specularMap': None}},
'5503490ffef3458eb874d6bfc8e21dac': {'model_module': 'jupyter-threejs',
'model_module_version': '0.3.0-alpha.0',
'model_name': 'AmbientLightModel',
'state': {'children': [],
'color': '#777777',
'position': [0.0, 0.0, 0.0],
'quaternion': [],
'scale': [1.0, 1.0, 1.0],
'up': [0.0, 1.0, 0.0]}},
'57f1f9c6670a4831b12111312d690fea': {'model_module': 'jupyter-threejs',
'model_module_version': '0.3.0-alpha.0',
'model_name': 'SceneModel',
'state': {'children': ['IPY_MODEL_10b0a2c543954ec99628b3ce0bcd84f7',
'IPY_MODEL_402cf0b394cb45389981f72c82e05343',
'IPY_MODEL_5503490ffef3458eb874d6bfc8e21dac'],
'position': [0.0, 0.0, 0.0],
'quaternion': [],
'scale': [1.0, 1.0, 1.0],
'up': [0.0, 1.0, 0.0]}},
'9cd3e202ba01477cb51db5d14e66d005': {'model_module': 'jupyter-threejs',
'model_module_version': '0.3.0-alpha.0',
'model_name': 'DirectionalLightModel',
'state': {'children': [],
'intensity': 0.5,
'position': [3.0, 5.0, 1.0],
'quaternion': [],
'scale': [1.0, 1.0, 1.0],
'up': [0.0, 1.0, 0.0]}},
'c7d34028e78a4d2dacbfad3eb7ebb752': {'model_module': 'jupyter-threejs',
'model_module_version': '0.3.0-alpha.0',
'model_name': 'SphereGeometryModel',
'state': {}}}
Loosely, it looks like:
model_name
needs to be renamed to type
state
dict has everything we needmaterials
, object
(for the scene?), geometries
, etc... unfortunately, this JSON format doesn't seem to be that well documentedIPY_MODEL
needs to be stripped from some of the UUIDsSo maybe something along the lines of ...
scene_json = []
for k, v in dependency_state(scene).items():
state = v['state']
if 'children' in state:
state['children'] = [child.replace('IPY_MODEL_', '') for child in state['children']]
scene_json.append({
'uuid': k,
'type': v['model_name'],
'data': state
})
I don't think this would work as-is, but it may be something along these lines.
Closing as stale.
I'm interested in a Pythonic way of generating three.js scenes into three.js's native JSON format for later rendering on the web, outside of a notebook environment.
What work would be required to add this sort of functionality to pythreejs?