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

discuss aframe like set of components #5

Closed
joshmarinacci opened this issue Oct 22, 2019 · 3 comments
Closed

discuss aframe like set of components #5

joshmarinacci opened this issue Oct 22, 2019 · 3 comments

Comments

@joshmarinacci
Copy link

ECSY - AFrame code mockup

AFrame

<html>
  <head>
    <script src="https://aframe.io/releases/0.9.2/aframe.min.js"></script>
  </head>
  <body>
    <a-scene>
      <a-box position="-1 0.5 -3" 
			rotation="0 45 0" 
			color="#4CC3D9"></a-box>
      <a-sphere position="0 1.25 -5" 
			radius="1.25" 
			color="#EF2D5E"></a-sphere>
      <a-cylinder 
				position="1 0.75 -3" 
				radius="0.5" 
				height="1.5" 
				color="#FFC65D"></a-cylinder>
      <a-plane position="0 0 -4" 
				rotation="-90 0 0" 
				width="4"
				height="4" 
				color="#7BC8A4"></a-plane>
      <a-sky color="#ECECEC"></a-sky>
    </a-scene>
  </body>
</html>

ECSY

const world = createWorld()
const core = world.createEntity()
core.addComponent(JScene,{}) // default settings

//make a 1x1x1 box
const box = world.createEntity()
	.addComponent(Mesh,{x:-1, y:0.5, z:-3, rx:0, ry:45, rz:0})
	.addComponent(BoxGeometry) // default w/h/d
	.addComponent(FlatMaterial,{color:'#4CC3D9'})

// make sphere
const sphere = world.createEntity()
	.addComponent(Mesh,{x:0 y:1.25, z:-5})
	.addComponent(SphereGeometry,{radius:1.25})
	.addComponent(FlatMaterial,{color:'#EF2D5E'})
//change the color
sphere.getMutableComponent(FlatMaterial).color = 'red'

//create a cylinder
world.createEntity()
	.addComponent(Mesh,{x:1, y:0.75, z:-3})
	.addComponent(CylinderGeometry,{radius:0.5, height:1.5})
	.addComponent(FlatMaterial,{color:'#FFC65D'})
	
// create a plane for the floor
world.createEntity()
	.addComponent(Mesh,{z:-4, rx:-90}), 
	.addComponent(PlaneGeometry, {width:4, height:4}})
	.addComponent(FlatMaterial,{color:'#7BC8A4'})

// add a skysphere
world.createEntity()
	.addComponent(SkySphere)
	.addComponent(FlatMaterial,{color:'#ECECEC'})

design

Split it into a series of components. One for the mesh for standard objects in the scene. Next is a geometry and a material. These mirror the underlying ThreeJS api. The API its very ‘regular’ meaning if you can set a property through the defaults then you can also set it as a property later by getting that component.

The Scene object wraps up all of the ThreeJS stuff. In general it should just do the right thing. If you use no custom settings then it will create a ThreeJS scene, render automatically at 60fps, build a container that will take over the screen, and add an enter WebXR button.

Interaction

User input and events were never very clean under A-Frame. To make the common case of a pointer work cleanly, I suggest a Clickable component. Add it to any object you want to be clickable and give it a callback.

const sphere = world.createEntity()
	addComponent(Mesh)
	addComponent(SphereGeometry,{radius: 1.0})
	addComponent(FlatMaterial,{color:'blue'})
	addComponent(Clickable, {onClick:(e)=>{
		console.log("the object",e.target,"was clicked")
	}})

You can add common Hover effects using a HoverEffect component. Ex:

const sphere ....
sphere.addComponent(HoverEffect,{tint:'red',outline:'white'})

The actual interaction will be done with raycasting that is hidden from the developer inside of an InteractionSystem. Mouse and Touch interaction is done by default. Just add MouseInput and TouchInput components on the scene entity. For VR controller you can add two VR controller components entities.

scene.addComponent(MousePointer)
scene.addComponent(TouchPointer)
scene.addComponent(VRController)

A toggle button could be implemented as

let plantEnabled = true
const plantMode = world.createEntity()
	.addComponent(Mesh)
	.addComponent(BoxGeometry)
	.addComponent(FlatMaterial,{color:'red'})

plantMode.add(Clickable,{onClick:(e)=>{
	plantEnabled = !plantEnabled
	e.targetEntity.getComponent(FlatMaterial).color = plantEnabled?'pink':'red'
}})

plantMode.add(HoverEffect,{tint:'white'})

For Gaze based interaction you can add a GazeCursor component to the scene entity and the right thing will happen. It will create a reticule with animation that triggers click events when you hover over a button, and it will be attached to the camera automatically.

As long as you stick the standard Clickable and Hoverable components then you don’t have to worry much about which cursor or pointer is active at any time.

By default the VRController component will be visualized as a laser pointer. If you have two physical controllers then the laser will be attached to whatever is the most recently used controller. You don’t need to do anything special for one vs two. You can attach VRController to a Mesh component to use a custom pointer object, but only in immersive mode.

entering and exiting VR

By default entering and exiting VR is handled for you automatically by the ThreeCore component and system. There is an enter / exit button created which handles detection and entering and exiting. A component is added to the entity that the ThreeCore is on called Immersive. You can create a system that will listen for Immersive added and removed to do something custom when entering and exiting.

Demo Components

Once this is in place we should be able to recreate some common A-Frame experiences.

Add a Stats component to the core to see stats on screen.

@joshmarinacci
Copy link
Author

Here's a demo showing a scene with a block and a clickable sphere

function setup() {
  let world = new World();
  world.registerSystem(ThreeSystem);
  world.registerSystem(InputSystem)

  const app = world.createEntity();
  app.addComponent(ThreeCore);

  world.execute(0.1, 0);

  const cube = world.createEntity();
  cube.addComponent(Mesh, {
    position: new Vector3(0, 0, -5),
    rotation: new Vector3(0.0, (45 * PI) / 180, 0)
  });
  cube.addComponent(BoxGeometry, { width: 2, height: 1, depth: 1 });
  cube.addComponent(FlatMaterial, { color: new Color("blue") });

  const sphere = world
    .createEntity()
    .addComponent(Mesh, { position: new Vector3(2, 2, -10) })
    .addComponent(SphereGeometry, { radius: 1.0 })
    .addComponent(FlatMaterial, { color: new Color("yellow") });
  sphere.getMutableComponent(FlatMaterial).color.set('green');

  let onState = false
  sphere.addComponent(Clickable, {
    onClick: e => {
      console.log("the object", e.targetEntity, "was clicked");
      onState = !onState
      e.targetEntity.getMutableComponent(FlatMaterial).color.set(onState?'yellow':'green')
    }
  });

  const clock = new Clock();
  const core = app.getComponent(ThreeCore);
  core.renderer.setAnimationLoop(() => {
    const delta = clock.getDelta();
    const elapsedTime = clock.elapsedTime;
    world.execute(delta, elapsedTime);
    core.renderer.render(core.scene, core.camera);
  });

live version here.

https://ecsy-three-test01.glitch.me/

@feiss
Copy link

feiss commented Oct 24, 2019

It looks cool!

having events it's very convenient and immediate, but not sure if it's the most ECSy way of doing it..

Regarding materials, I think there would be cool to have a global material repository, where you create the materials and give them a name, and then assign the material to the entities using the name. Something like (not very ECSy and ugly, but helps to explain):

materialManager.addMaterial('bricks', new THREE.MeshStandardMaterial(...));
...
const wall = world.createEntity().addComponent(Material, {name: 'bricks`});

This helps in many ways:

  • Makes the dev more aware and careful before creating too many materials (that can look the same or almost), so helps to optimize the app
  • Helps to organize and manage the materials in much structured way
  • Helps to reuse the same material in multiple meshes (allowing to make general changes to them)

But in general, it looks like a very convenient and nice API to work with :)

@robertlong
Copy link
Member

Moving discussion to #36

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

3 participants