initial commit

This commit is contained in:
Geoff Doty 2018-03-08 15:09:02 -05:00
commit a0a489f1c6
15 changed files with 1035 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
node_modules/
*/js/vendor/

15
README.md Normal file
View File

@ -0,0 +1,15 @@
# Canvas Editor
An *alternative* [Playcanvas Engine](https://github.com/playcanvas/engine) Editor
## Why
- Better understanding the internals of [Playcanvas]()
- Built larger [RiotJS]() application
- Convert to standalone app using [Electron]()
- Store assets *locally*
- Free
## Is it Ready
NO!

55
assets/css/styles.css Normal file
View File

@ -0,0 +1,55 @@
/* Tables - for Properties */
caption {height: 2em; background-color: #404040; line-height: 2em; color: #EEE; text-align: left;padding-left: 1em;}
table {width: 100%;border: 1px solid #CCC; border-spacing: 0;}
tr {height: 1.5em; line-height: 1.5em;}
th {
font-weight: 100;
font-size: .8em;
text-align: left;
padding-left: 1em;
width: 100px;
background-color: #EEE;
border-bottom: 1px solid #CCC;
border-right: 1px solid #CCC;
}
td {background-color: #FFF; border-bottom: 1px solid #CCC;}
/* Buttons */
.button {
box-sizing: border-box;
min-height: 1.5em;
line-height: 1.5em;
width:100%;
border: 1px solid #AAA;
color: #222;
cursor: pointer;
}
/* Fields */
.field::placeholder {
color: #CCC;
text-align: right;
font-size: .8em;
}
.field {
min-height: 100%;
width: 100%;
line-height: 1.5em;
flex: 1;
border: none;
padding: 0 .5em;
border-right: 1px solid #CCC;
}
.field:last-child {
border: none;
}
.field-group {
display: flex;
justify-content: space-around;
}
/* Utilities */
.is-small {font-size: .8em;}

19
assets/js/app.js Normal file
View File

@ -0,0 +1,19 @@
'use strict';
const app = {
version: 0,
debug: true,
// http://paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/
log: function() {
log.history = log.history || []; // store logs to an array for reference
log.history.push(arguments);
if(this.debug){
console.log( Array.prototype.slice.call(arguments) );
}
}
};
riot.mixin({
log: app.log,
relay: riot.observable()
});

View File

@ -0,0 +1,67 @@
<Application>
<div>
<canvas ref="app" id="app"></canvas>
</div>
<style></style>
<script>
var tag = this;
tag.on('mount', function() {
var canvas = tag.refs.app;
var app = new pc.Application(canvas, {});
app.start();
app.setCanvasFillMode(pc.FILLMODE_NONE, 640, 480);
app.setCanvasResolution(pc.RESOLUTION_AUTO);
// ensure canvas is resized when window changes size
window.addEventListener('resize', function() {
app.resizeCanvas();
});
// create box entity
var cube = new pc.Entity('cube');
cube.addComponent('model', {
type: 'box'
});
// create camera entity
var camera = new pc.Entity('camera');
camera.addComponent('camera', {
clearColor: new pc.Color(0.1, 0.1, 0.1)
});
// create directional light entity
var light = new pc.Entity('light');
light.addComponent('light');
// add text
var text = new pc.Entity('text');
text.addComponent('element', {
type: 'text',
text: 'hello World'
});
// add to hierarchy
app.root.addChild(cube);
app.root.addChild(camera);
app.root.addChild(light);
app.root.addChild(text);
// set up initial positions and orientations
camera.setPosition(0, 0, 3);
light.setEulerAngles(45, 0, 0);
tag.relay.trigger('app', app);
// register a global update event
app.on('update', function (dt) {
cube.rotate(10 * dt, 20 * dt, 30 * dt);
});
});
</script>
</Application>

View File

@ -0,0 +1,141 @@
<Hierarchy>
<div class="container">
<div class="header">
<div class="hierarchy">
<div class="is-center">
<strong class="is-lead">{project.name}</strong>
</div>
<button onclick={add}>Add</button>
<button onclick={play}>Play</button>
<button onclick={build}>Build</button>
</div>
<div class="container">
<input type="text" placeholder="Search">
</div>
<div>
<ul>
<li>Root</li>
<ul>
<li each={entity in project.entities} class="is-title">
<button class="button" onclick="{selectEntity.bind(e, entity)}">{entity.name}</button>
</li>
</ul>
</ul>
</div>
</div>
</div>
<style>
.hierarchy {
display:flex;
justify-content: space-around;
align-items: center;
height: 3em;
background-color: #404040;
color: #EEE;
line-height: 3em;
text-align: center;
}
ul,ol {
list-style: none;
padding-left: .5em;
}
.is-lead {
font-weight: bold;
font-size: 1.2em;
}
.is-title {
text-transform: capitalize;
}
.is-center {
text-align: center;
}
.container {display: flex; flex-flow: column}
.button {
text-align:left;
padding-left: .5em;
height: 1.5em;
line-height: 1.5em;
width: 100%;
border:none;
background:transparent;
font-size: 16px;
text-transform: capitalize;
cursor: pointer;
}
.button:hover {
background-color:#DDD;
}
.button:active, .button:focus {
box-sizing: border-box;
border:none;
outline:none;
background:#CCC;
}
.selected {
background-color: khaki;
}
</style>
<script>
var tag = this;
tag.project = {};
tag.project.name = 'Hiearchy';
tag.project.entities = [];
tag.entity = null;
tag.ui = null;
tag.add = function() {};
tag.play = function() {};
tag.build = function() {};
// show select hierarchy item
tag.selected = function(current) {
// if previously set
if(tag.ui) {
tag.ui.classList.remove('selected');
}
// set active ui element
tag.ui = current;
// reflect in ui
current.classList.add('selected');
}
tag.selectEntity = function(entity, el) {
tag.relay.trigger('entity-selected', entity);
// update ui
tag.selected(el.target);
// update data
tag.entity = entity;
}
tag.relay.on('app', function(app) {
tag.project.name = app.root.name;
tag.project.entities = app.root._children;
tag.update();
});
tag.on('mount', function() {
tag.project = {};
tag.project.name = 'Hiearchy';
tag.project.entities = [];
tag.project.items = [];
});
</script>
</Hierarchy>

View File

@ -0,0 +1,119 @@
<Component-All>
<section>
<table if="{component}">
<caption>Camera</caption>
<tr>
<th>Clear Buffers</th>
<td>
<div class="field-group is-small">
<label for=""><input type="checkbox"> Color</label>
<label for=""><input type="checkbox"> Depth</label>
</div>
</td>
</tr>
<tr>
<th>Clear Color</th>
<td>
<div class="field-group">
<input type="text" class="field" placeholder="Hex">
<input type="color" name="" id="">
</div>
</td>
</tr>
<tr>
<th>Projection</th>
<td>
<select class="field" name="type">
<option each="{projection in ui.projections}" value="{projection}" selected="{projection === component}">{projection}</option>
</select>
</td>
</tr>
<tr>
<th>Frustum Culling</th>
<td>
<input type="checkbox" style="justify-content: flex-start">
</td>
</tr>
<tr>
<th>Field of View</th>
<td>
<div class="field-group">
<input class="field" type="text" style="flex: 0 0 34px;">
<input class="field" type="range" name="" id="" style="width:90%;">
</div>
</td>
</tr>
<tr>
<th>Clip</th>
<td>
<div class="field-group">
<input class="field" type="text" placeholder="Near">
<input class="field" type="text" placeholder="Far">
</div>
</td>
</tr>
<tr>
<th>Priority</th>
<td>
<input class="field" type="text">
</td>
</tr>
<tr>
<th>Viewport</th>
<td>
<div class="field-group">
<input class="field" type="text" placeholder="X">
<input class="field" type="text" placeholder="Y">
<input class="field" type="text" placeholder="W">
<input class="field" type="text" placeholder="H">
</div>
</td>
</tr>
</table>
</section>
<style>
a {
justify-content: flex-start
}
</style>
<script>
var tag = this;
tag.ui = {}
tag.component = null;
tag.getProperties = function() {
var _props = [];
tag.component.forEach(function(prop) {
let name = Object.keys(prop);
let value = prop;
_props.push[{key: name, value: value}];
});
console.log('Properties', _props);
}
tag.on('*', function(event) {
console.log('LIFE CYCLE', event);
})
tag.on('update', function() {
});
tag.on('mount', function() {
tag.component = opts.component.c;
tag.ui.name = Object.keys(opts.component.c)[0];
tag.ui.properties = getProperties();
tag.update();
});
</script>
</Component-All>

View File

@ -0,0 +1,93 @@
<Component-Camera>
<section>
<table if="{component}">
<caption>Camera</caption>
<tr>
<th>Clear Buffers</th>
<td>
<div class="field-group is-small">
<label for=""><input type="checkbox"> Color</label>
<label for=""><input type="checkbox"> Depth</label>
</div>
</td>
</tr>
<tr>
<th>Clear Color</th>
<td>
<div class="field-group">
<input type="text" class="field" placeholder="Hex">
<input type="color" name="" id="">
</div>
</td>
</tr>
<tr>
<th>Projection</th>
<td>
<select class="field" name="type">
<option each="{projection in ui.projections}" value="{projection}" selected="{projection === component}">{projection}</option>
</select>
</td>
</tr>
<tr>
<th>Frustum Culling</th>
<td>
<input type="checkbox" style="justify-content: flex-start">
</td>
</tr>
<tr>
<th>Field of View</th>
<td>
<div class="field-group">
<input class="field" type="text" style="flex: 0 0 34px;">
<input class="field" type="range" name="" id="" style="width:90%;">
</div>
</td>
</tr>
<tr>
<th>Clip</th>
<td>
<div class="field-group">
<input class="field" type="text" placeholder="Near">
<input class="field" type="text" placeholder="Far">
</div>
</td>
</tr>
<tr>
<th>Priority</th>
<td>
<input class="field" type="text">
</td>
</tr>
<tr>
<th>Viewport</th>
<td>
<div class="field-group">
<input class="field" type="text" placeholder="X">
<input class="field" type="text" placeholder="Y">
<input class="field" type="text" placeholder="W">
<input class="field" type="text" placeholder="H">
</div>
</td>
</tr>
</table>
</section>
<style>
a {
justify-content: flex-start
}
</style>
<script>
var tag = this;
tag.ui = {}
tag.ui.projections = ['perspective', 'orthographic'];
tag.component = {};
tag.on('mount', function() {
tag.update();
});
</script>
</Component-Camera>

View File

@ -0,0 +1,28 @@
<Components>
<div each="{component in components}">
<div if="{component === 'model'}"><Component-Model component={entity.model} /></div>
<div if="{component === 'camera'}"><Component-Camera/></div>
<div if="{component === 'light'}"><Component-Light/></div>
<div if="{component === 'element'}"><Component-Element/></div>
</div>
<script>
var tag = this;
tag.on('update', function() {
// determine components in entity
tag.components = Object.keys(opts.entity.c);
tag.entity = opts.entity;
});
tag.on('mount', function() {
// determine components in entity
tag.components = Object.keys(opts.entity.c);
tag.entity = {};
tag.update();
});
</script>
</Components>

View File

@ -0,0 +1,160 @@
<Component-Element>
<section>
<table if="{element}">
<caption>Element</caption>
<tr>
<th>Type</th>
<td>
<select class="field" name="type">
<option each="{type in element.types}" value="{type}" selected="{type === component}">{type}</option>
</select>
</td>
</tr>
<tr>
<th>Preset</th>
<td>
<select class="field" name="type">
<option each="{preset in element.presets}" value="{preset}" selected="{preset === component}">{preset}</option>
</select>
</td>
</tr>
<tr>
<th>Anchor</th>
<td>
<div class="field-group">
<input class="field" type="text" placeholder="T">
<input class="field" type="text" placeholder="R">
<input class="field" type="text" placeholder="B">
<input class="field" type="text" placeholder="L">
</div>
</td>
</tr>
<tr>
<th>Pivot</th>
<td>
<div class="field-group">
<input class="field" type="text" placeholder="hor">
<input class="field" type="text" placeholder="vert">
</div>
</td>
</tr>
<tr>
<th>Auto-Size</th>
<td>
<div class="field-group is-small">
<label for=""><input type="checkbox"> Width</label>
<label for=""><input type="checkbox"> Height</label>
</div>
</td>
</tr>
<tr>
<th>Size</th>
<td>
<div class="field-group">
<input class="field" type="text" placeholder="Width">
<input class="field" type="text" placeholder="Height">
</div>
</td>
</tr>
<tr>
<th>Margin</th>
<td>
<div class="field-group">
<input class="field" type="text" placeholder="T">
<input class="field" type="text" placeholder="R">
<input class="field" type="text" placeholder="B">
<input class="field" type="text" placeholder="L">
</div>
</td>
</tr>
<tr>
<th>Alignment</th>
<td>
<div class="field-group">
<input class="field" type="text" placeholder="hor">
<input class="field" type="text" placeholder="vert">
</div>
</td>
</tr>
<tr>
<th>Text</th>
<td>
<textarea name="" id="" style="width: 100%;height: 4em;"></textarea>
</td>
</tr>
<tr>
<th>Font Size</th>
<td>
<div class="field-group">
<input class="field" type="text" placeholder="hor">
</div>
</td>
</tr>
<tr>
<th>Line Height</th>
<td>
<div class="field-group">
<input class="field" type="text" placeholder="hor">
</div>
</td>
</tr>
<tr>
<th>Spacing</th>
<td>
<div class="field-group">
<input class="field" type="text" placeholder="hor">
</div>
</td>
</tr>
<tr>
<th>Font</th>
<td>
<div class="field-group">
<input class="field" type="text" placeholder="hor">
</div>
</td>
</tr>
<tr>
<th>Font</th>
<td>
<div class="field-group">
<button onclick="{loadFont}">...</button>
<input type="file" name="" id="font" style="display:none" onchange="{updateFont}">
<input type="text" id="font-file" disabled>
</div>
</td>
</tr>
<tr>
<th>Color</th>
<td>
<div class="field-group">
<input type="text" class="field" placeholder="Hex">
<input type="color" name="" id="">
</div>
</td>
</tr>
</table>
</section>
<style>
textarea {
min-width: 100%;
max-width: 182px;
margin:0;
padding:0;
border:none;
}
</style>
<script>
var tag = this;
tag.element = {};
tag.element.types = ['text', 'image', 'group'];
tag.on('mount', function() {
tag.update();
});
</script>
</Component-Element>

View File

@ -0,0 +1,99 @@
<Component-Light>
<section>
<table if="{light}">
<caption>Light</caption>
<tr>
<th>Type</th>
<td>
<select class="field" name="type">
<option each="{type in light.types}" value="{type}" selected="{type === component}">{type}</option>
</select>
</td>
</tr>
<tr>
<th>Color</th>
<td>
<div class="field-group">
<input type="text" class="field" placeholder="Hex">
<input type="color" name="" id="">
</div>
</td>
</tr>
<tr>
<th>Intensity</th>
<td>
<div class="field-group">
<input class="field" type="text" style="flex: 0 0 34px;">
<input class="field" type="range" name="" id="" style="width:90%;">
</div>
</td>
</tr>
<tr>
<th>States</th>
<td>
<div class="field-group is-small">
<label for=""><input type="checkbox"> Static</label>
</div>
</td>
</tr>
<tr>
<th>Lightmap</th>
<td>
<div class="field-group is-small">
<label for=""><input type="checkbox"> Bake</label>
<label for=""><input type="checkbox" disabled> Direction</label>
</div>
</td>
</tr>
<tr>
<th>Affect</th>
<td>
<div class="field-group is-small">
<label for=""><input type="checkbox"> Non-Baked</label>
<label for=""><input type="checkbox"> Baked</label>
</div>
</td>
</tr>
<tr>
<th>Shadows</th>
<td>
<div class="field-group is-small">
<label class="field" for=""><input type="checkbox"></label>
</div>
</td>
</tr>
<tr>
<th>Update Mode</th>
<td></td>
</tr>
<tr>
<th>Resolution</th>
<td></td>
</tr>
<tr>
<th>Distance</th>
<td></td>
</tr>
<tr>
<th>Shadow Type</th>
<td></td>
</tr>
<tr>
<th>Bias</th>
<td></td>
</tr>
</table>
</section>
<script>
var tag = this;
tag.light = {};
tag.light.types = ['directional', 'spot', 'point'];
tag.on('mount', function() {
tag.update();
});
</script>
</Component-Light>

View File

@ -0,0 +1,69 @@
<Component-Model>
<section>
<table if="{component}">
<caption>Model</caption>
<tr>
<th>Type</th>
<td>
<select class="field" name="type">
<option each="{type in ui.types}" value="{type}" selected="{type === component}">{type}</option>
</select>
</td>
</tr>
<tr>
<th>Material</th>
<td>
<div class="field-group">
<button onclick="{loadMaterial}">...</button>
<input type="file" name="" id="material" style="display:none" onchange="{updateMaterial}">
<input type="text" id="material-file" disabled>
</div>
</td>
</tr>
<tr>
<th>Shadows</th>
<td>
<div class="field-group is-small">
<label for=""><input type="checkbox" checked="{component.castShadows}"> Cast</label>
<label for=""><input type="checkbox" checked="{component.castShadowsLightmap}"> Light</label>
<label for=""><input type="checkbox" checked="{component.receiveShadows}"> Recieve</label>
</div>
</td>
</tr>
<tr>
<th>States</th>
<td>
<div class="field-group is-small">
<label for=""><input type="checkbox" checked="{component.isStatic}"> Static</label>
<label for=""><input type="checkbox" checked="{component.lightmapped}"> Light Mapped</label>
</div>
</td>
</tr>
</table>
</section>
<script>
var tag = this;
tag.component = {};
tag.ui = {};
tag.ui.types = ['box', 'capsole', 'sphere', 'cylinder', 'cone', 'plane'];
tag.updateMaterial = function() {
var el = document.getElementById('material');
document.getElementById('material-file').value = el.value;
}
tag.loadMaterial = function() {
document.getElementById('material').click();
}
tag.on('update', function() {});
tag.on('mount', function() {
tag.component = opts.component;
tag.update();
});
</script>
</Component-Model>

View File

@ -0,0 +1,93 @@
<Properties>
<div>
<div class="entity">
<table if={entity}>
<caption>Entity</caption>
<tr>
<th>Enabled</th>
<td>
<input type="checkbox" checked={entity._enabled}>
</td>
</tr>
<tr>
<th>Name</th>
<td>
<div class="field-group">
<input class="field" type="text" value="{entity.name}">
</div>
</td>
</tr>
<tr>
<th>Tags</th>
<td>
<div class="field-group">
<input type="text" class="field" placeholder="Add Tag">
</div>
</td>
</tr>
<tr>
<th>Position</th>
<td>
<div class="field-group">
<input type="text" class="field" placeholder="X" value="{entity.position.data[0]}">
<input type="text" class="field" placeholder="Y" value="{entity.position.data[1]}">
<input type="text" class="field" placeholder="Z" value="{entity.position.data[2]}">
</div>
</td>
</tr>
<tr>
<th>Rotation</th>
<td>
<div class="field-group">
<input type="text" class="field" placeholder="X" value="{entity.rotation.x}">
<input type="text" class="field" placeholder="Y" value="{entity.rotation.y}">
<input type="text" class="field" placeholder="Z" value="{entity.rotation.z}">
</div>
</td>
</tr>
<tr>
<th>Scale</th>
<td>
<div class="field-group">
<input type="text" class="field" placeholder="X" value="{entity.localScale.data[0]}">
<input type="text" class="field" placeholder="Y" value="{entity.localScale.data[1]}">
<input type="text" class="field" placeholder="Z" value="{entity.localScale.data[2]}">
</div>
</td>
</tr>
<tfoot>
<tr>
<td colspan="2">
<button class="button">Add Component</button>
</td>
</tr>
</tfoot>
</table>
<div if={entity}>
<Components entity={entity}></Components>
</div>
</div>
</div>
<style></style>
<script>
var tag = this;
tag.relay.on('entity-selected', function(entity) {
console.log('entity-selected', entity);
// store entity
tag.entity = entity;
// determine "c"omponent type
tag.component = Object.keys(entity.c)[0];
tag.update();
});
tag.on('mount', function() {
});
</script>
</Properties>

View File

@ -0,0 +1,20 @@
# Properties
currently, the UI has a `properties` sidebar that includes an `entity` section and one or more 'components`
- properties
- entity
- components
- component
## Folder Sturucture
```
properties/
properties.tag.html
componenents/
componenets.tag.html <-- component load
model.tag.html <-- component
```

55
index.html Normal file
View File

@ -0,0 +1,55 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Editor</title>
<link rel="stylesheet" href="assets/css/styles.css">
<script src="assets/js/vendor/riot+compiler.js"></script>
<script src="assets/js/vendor/playcanvas-latest.js"></script>
<style>
body {
margin:0;
padding:0;
box-sizing: border-box;
font-size: 16px;
font-family: sans-serif;
color: #777;
}
:root {
--dark: #404040;
}
.page-container {display:flex; height: 100vh;}
.left,.right {flex:0 0 300px; background-color: #EEE;min-width:200px;}
.left {flex:0 0 240px;}
.view {flex:1}
</style>
</head>
<body>
<div class="page-container">
<section class="left">
<Hierarchy />
</section>
<section class="view">
<Application />
</section>
<section class="right">
<Properties />
</section>
</div>
<script type="riot/tag" src="assets/tags/hierarchy.tag.html"></script>
<script type="riot/tag" src="assets/tags/application.tag.html"></script>
<script type="riot/tag" src="assets/tags/property/components/model.tag.html"></script>
<script type="riot/tag" src="assets/tags/property/components/camera.tag.html"></script>
<script type="riot/tag" src="assets/tags/property/components/light.tag.html"></script>
<script type="riot/tag" src="assets/tags/property/components/element.tag.html"></script>
<script type="riot/tag" src="assets/tags/property/components/components.tag.html"></script>
<script type="riot/tag" src="assets/tags/property/properties.tag.html"></script>
<script>
riot.mount('*');
</script>
<script src="assets/js/app.js"></script>
</body>
</html>