3D Renderer
by Jim Buck
HTML
<script src="//cdnjs.cloudflare.com/ajax/libs/crafty/0.5.3/crafty-min.js"></script>
<p>The red box will follow your mouse (x and z). Scroll up and down to change the height of the box (y).</p>
<p>Notice how much the box moves when it is close to the camera? Now try moving it when it is very var from the camera. Notice the difference?</p>
<div class="clearfix">
<div id="topDownDisplay" class="display"></div>
<div id="cameraDisplay" class="display"></div>
</div>
<div id="coords"><div/>
CSS
html, body {
margin: 8px;
font-family: 'Helvetica';
background-color: #ccc;
}
.clearfix:after {
content: "";
display: table;
clear: both;
}
.display {
float: left;
position: relative;
width:480px;
height:480px;
border:1px solid #000;
overflow:hidden;
background-color: #fff;
}
.display+.display {
margin-left: 8px;
}
#topDownDisplay {
}
#topDownDisplay:before {
content: 'Top Down';
font-weight: bold;
}
#cameraDisplay {
}
#cameraDisplay:before {
content: 'Camera';
font-weight: bold;
}
TypeScript
var running;
var redraw;
var entities = [];
var mainEntity;
var camera = {
fieldOfView: 3,
nearClippingPlane: 3,
farClippingPlane: 460,
scrollSpeed: -10
};
var topDownDisplay = $('#topDownDisplay');
var cameraDisplay = $('#cameraDisplay');
var coords = $('#coords');
$(function(){
reset();
start();
});
topDownDisplay.on('mousemove', function(e){
var offset = topDownDisplay.offset();
var totalHeight = topDownDisplay.height();
mainEntity.x = e.pageX - offset.left;
mainEntity.z = (totalHeight - (e.pageY - offset.top));
redraw = true;
}).on('mousewheel DOMMouseScroll', function(e){
if(e.originalEvent.wheelDelta/120 > 0) {
mainEntity.y = mainEntity.y + camera.scrollSpeed;
} else {
mainEntity.y = mainEntity.y - camera.scrollSpeed;
}
redraw = true;
}).click(function(){
mainEntity.color = randColor();
});
function reset(){
running = false;
mainEntity = null;
entities = [];
topDownDisplay.empty();
cameraDisplay.empty();
mainEntity = createEntity(100, 200, 320, 240, 10, null, function(e){
coords.text('x: ' + e.x + ' z: ' + e.z + ' y: ' + e.y);
});
}
function start(){
if(running)
return;
running = true;
tick();
}
function tick() {
if(!running) return;
if(redraw){
redraw = false;
update();
draw();
}
requestAnimationFrame(tick);
}
function stop(){
running = false;
}
function update(){
// Update positions...
for(var e in entities){
entities[e].update(entities[e]);
}
}
function draw(){
// Clear and redraw...
topDownDisplay.empty();
cameraDisplay.empty();
entities.sort(function(a,b){return a.z-b.z});
drawCamera();
for(var e in entities) {
drawTopDown(entities[e]);
drawFront(entities[e]);
}
}
function createEntity(height, width, x,y,z, color, callback){
var entity = {
id: entities.length,
x, y, z,...