Text representation of scene objects - markdown for threejs scene

Has anyone thought about having a text representation of a scene that gets converted into threejs code or previewed in a live threejs scene canvas?

Similar to how markdown can be used as a simplified text representation of common HTML patterns.

Any suggestions for a text file format? I was thinking something YAML like. Object3Ds can be nested and properties can be set for each instance. Here’s an idea for a grammar

Scene MyScene
+ PerspectiveCamera (camera1)
  - fov: 75
  - aspect: 1
  - near: 0.1
  - far: 1000
  - position: 0 0 5
# Geometry Box (box1)
# Material Basis (red)
    - color: #ff0000
- Children
  + Mesh box1 red
    - position: 1 0 0
    > onPointerDown(event) {
        console.log('Pointer down event:', event);
      }
# defines an object such as a material or geometry so that it can be referenced multiple times later
+ adds supported object types
- set properties of the parent object
> adds event listeners to parent object
document and windows would be pre-existing objects for adding event listeners.

Still got a lot to work out, but wanted the throw the idea out there.

JSON :eyes: ?

2 Likes

If not the existing JSON format, R3F is probably what you’re looking for.

1 Like

It might be good to be possible to have a Three.js scene into whatever text format, but to include only the non-default values and properties. A full JSON dump might not be what @anidivr is having in mind. Also, text → scene is half of the story, the other half is scene → text.

1 Like

That’s how ChatGPT is slowly replacing us.

1 Like

Tried it with xml + xslt :sweat_smile:

So, this:

<?xml version="1.0" encoding="UTF-8"?>
<root>
	<Scene name="scene">
		<children>
			<Mesh name="mesh">
				<BoxGeometry name="geometry">
					<props>
						<width>2</width>
						<heigth>2</heigth>
						<depth>2</depth>
						<widthSegments>2</widthSegments>
						<heightSegments>2</heightSegments>
						<depthSegments>2</depthSegments>
					</props>
				</BoxGeometry>
				<MeshBasicMaterial name="material">
					<propNames>
						<color>"red"</color>
						<wireframe>true</wireframe>
					</propNames>
				</MeshBasicMaterial>
				<position>
					<x>0</x>
					<y>0</y>
					<z>0</z>
				</position>
			</Mesh>
		</children>
	</Scene>
	<PerspectiveCamera name="camera">
		<props>
			<fov>45</fov>
			<aspect>innerWidth / innerHeight</aspect>
			<near>1</near>
			<far>10</far>
		</props>
		<position>
			<x>2</x>
			<y>3</y>
			<z>5</z>
		</position>
		<lookAt>
			<x>0</x>
			<y>0</y>
			<z>0</z>
		</lookAt>
	</PerspectiveCamera>
	<WebGLRenderer name="renderer">
		<propNames>
			<antialias>true</antialias>
		</propNames>
	</WebGLRenderer>
</root>

turned into code:

<!DOCTYPE html SYSTEM "about:legacy-compat"><html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>ThreeXML</title>
<style>
					body{
						overflow: hidden;
						margin: 0;
					}
				</style>
</head>
<body><script async="true" src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script><script type="importmap">
				  {
					"imports": {
					  "three": "https://unpkg.com/three@0.153.0/build/three.module.js"
					}
				  }	
				</script><script type="module">
					import * as THREE from "three";
					
					
		let scene = new THREE.Scene();
		
		
		
		let geometry = new THREE.BoxGeometry(
			2, 2, 2, 2, 2, 2
		);
		
	
		
		let material = new THREE.MeshBasicMaterial({
			color: "red", wireframe: true
		});
		
	
		
		let mesh = new THREE.Mesh(
			geometry,
			material
		);
		
		scene.add(mesh);
	
		
		let camera = new THREE.PerspectiveCamera(
			45, innerWidth / innerHeight, 1, 10
		);
		
	camera.position.set(2, 3, 5);camera.lookAt(0, 0, 0);
		
		let renderer = new THREE.WebGLRenderer({
			antialias: true
		});
		
	renderer.setSize(innerWidth, innerHeight);
		document.body.appendChild(renderer.domElement);
		
		renderer.setAnimationLoop(() => {
			renderer.render(scene, camera);
		});
		
	</script></body>
</html>


that produces this:

3 Likes

I asked it to turn @anidivr`s suggested markdown to a ThreeJS scene, here is the answer:

1 Like

I came across povray scene description language.

Looks pretty clean and JSON like. Would clearly be a subset and deviate where necessary.

torus { 1, 0.5 }
text { ttf “timrom.ttf” , “POV-Ray” , 1 , 0 }
sphere { <0, 1, 2>, 1 translate <1, 0, 0> texture { pigment { rgb <1, 0, 0> } } }
light_source { <2, 4, -3> rgb <1, 1, 1> }
box { <0, 0, 0>, <1, 1, 1> translate <2, 0, 0> scale <1, 2, 1> rotate <0, 0, 45> }

I’m not suggesting to build a ray tracer, but many of the concepts translate quite nicely into threejs equivalents.

Should be possible to incorporate CSG and a scriptable states/events like spline.design

I’m thinking of calling it ThreeDL - Three Definition Language

Here’s an screenshot of an early prototype. Syntax is like povray, but more aligned with threejs. The scene is updated as text changes are made.

Planning to add support for all major features of three, plus CSG. Also, planning to have a button to export as equivalent javascript code.

Consider functional programming for a moment, where the outcome is a function of state:

function add(a, b) {
  return a + b
}

add(1, 2) 
// the outcome is deterministically 3

Now FP can be expressed declaratively. This is in a nutshell what React is for. Declaritivity alone is not enough, the view (or scene) has to be the outcome of a function of state, or else it would only be a blob in a readable format. As a function it becomes reusable and actionable, this was the birth of the component:

function Box({ color, ...props }) {
  return (
    <mesh {...props}>
     <boxGeometry />
     <meshBasicMaterial color={color} />
    </mesh>
  )
}

<Box position={[1, 2, 3]} color="red" onClick={() => console.log("!")} /> 
// the outcome is deterministically a red box

You can try it here:

The JSX format is an optional DSL like Typescript, it makes nested functions readable. These functions are similar to createElement in the DOM-Api. The above for instance is the same as:

return createElement("mesh", { ...props, children: [
  createElement("boxGeometry"), 
  createElement("meshBasicMaterial", { color }),
]})

I’ll give you some examples you might want to try,

A larger Sketchfab model that was turned into JSX by gltfjsx. It extracts a models scene and by that point it’s yours to control.

Interactive and declarative CSG.

The eco systems this made possible.

Mixing HTML and interactive buttons with Threejs

In essence this is 1:1 what you describe in the first post, scene graph events and all. The component paradigm exists for 12 years, the renderer for Threejs (Fiber) for 3 years.