|
// conceptualized and written by andi smithers |
|
// copyright andi smithers. |
|
// freely distributable in whole or in partial |
|
// please retain credit and comment if distributed |
|
// thank you. |
|
|
|
// constant options |
|
const focalDepth = 80; |
|
const focalPoint = 256; |
|
|
|
|
|
// variables |
|
var centreX; |
|
var centreY; |
|
var mouseX; |
|
var mouseY; |
|
var spawnX; |
|
var spawnY; |
|
var frameCount=0; |
|
var targetLayer = 0.25; |
|
var layer = 0.25; |
|
// test multiple groups |
|
|
|
|
|
// initialization |
|
|
|
function init() |
|
{ |
|
// setup canvas and context |
|
canvas = document.getElementById('hemisphere'); |
|
context = canvas.getContext('2d'); |
|
|
|
// set canvas to be window dimensions |
|
resize(); |
|
|
|
// create event listeners |
|
canvas.addEventListener('mousemove', mouseMove); |
|
canvas.addEventListener('click', mouseClick); |
|
window.addEventListener('resize', resize); |
|
|
|
// initialze variables |
|
} |
|
|
|
|
|
// input functions |
|
|
|
function mouseMove(event) |
|
{ |
|
var rect = canvas.getBoundingClientRect(); |
|
|
|
mouseX = event.clientX - rect.left, |
|
mouseY = event.clientY - rect.top |
|
} |
|
|
|
function mouseClick() |
|
{ |
|
targetLayer-=0.25; |
|
if (targetLayer <0.0) targetLayer = 0.75; |
|
} |
|
|
|
function resize() |
|
{ |
|
canvas.width = window.innerWidth; |
|
canvas.height = window.innerHeight; |
|
// compute centre of screen |
|
centreX = canvas.width/2; |
|
centreY = canvas.height/2; |
|
} |
|
|
|
|
|
// rendering functions |
|
|
|
function render() |
|
{ |
|
|
|
context.fillStyle = 'black'; |
|
context.clearRect(0, 0, canvas.width, canvas.height); |
|
|
|
RenderBlueMarble(); |
|
|
|
context.globalAlpha = 1.0; |
|
context.font = '20pt Calibri'; |
|
context.fillStyle = 'rgb(255,255,255)'; |
|
context.textAlign = "center"; |
|
context.fillText('Hemi-Sphere 2D rendering', canvas.width/2, 20); |
|
context.fillText('Blue Marble Cutaway', canvas.width/2, canvas.height-20); |
|
context.font = '14pt Calibri'; |
|
context.fillText('(click to cycle layers)', canvas.width/2, 40); |
|
|
|
} |
|
|
|
function RenderBlueMarble() |
|
{ |
|
// interporlate layer changes so its nice and smooth |
|
var dx = targetLayer-layer; |
|
layer+=dx*0.1; |
|
|
|
// rotate |
|
angle=(angle+0.01)%(Math.PI*2); |
|
|
|
var x = centreX; |
|
var y = centreY; |
|
var radius = canvas.height/4; |
|
|
|
if (angle>Math.PI*0.5 && angle<Math.PI*1.5) RenderCore(x, y, radius*layer, layer); |
|
RenderHemisphere(x, y, radius, 4, '#0000ff'); |
|
if (!(angle>Math.PI*0.5 && angle<Math.PI*1.5)) RenderCore(x, y, radius*layer, layer); |
|
} |
|
|
|
function RenderCore(x, y, r, layer) |
|
{ |
|
var outerColor = 'rgb(255,'+Math.floor(255-255*layer)+',0)'; |
|
angle=(angle+Math.PI)%(Math.PI*2); |
|
RenderHemisphere(x, y, r, 1, outerColor); |
|
angle=(angle+Math.PI)%(Math.PI*2); |
|
} |
|
|
|
// star base construction - eer well it was but its a cool blue marble demo for now |
|
// to rotate horizontally switch out the x/y registers. I should probably make this generic .. however just want to use this for the starbase construction in star-raiders |
|
var angle = 0; |
|
function RenderHemisphere(x, y, rad, slices, outerColor) |
|
{ |
|
// really bad hack to change orientation of the hemisphere |
|
// rotate the object 90 degrees and then compensate for the centre x/y changes |
|
context.save(); |
|
context.rotate(Math.PI*0.5); |
|
context.translate(-x+y,-y-x); |
|
|
|
m = 0.5522848; |
|
|
|
// molten core |
|
for (var r=rad; r>0; r-=(rad/slices)) |
|
{ |
|
|
|
tr =r*Math.cos(angle); // top hemisphere angle *rad |
|
br =r*Math.cos(angle); // bottom angle *rad |
|
|
|
xkr = m*r; // kappa * r |
|
tkr = m*tr; // top height * kappa |
|
bkr = m*br; // bottom height * kappa |
|
|
|
context.beginPath() |
|
context.moveTo(x+r, y) |
|
context.bezierCurveTo(x+r, y-tkr, x+xkr, y-tr, x, y-tr) // top r |
|
context.bezierCurveTo(x-xkr, y-tr, x-r, y-tkr, x-r, y) // top l |
|
context.bezierCurveTo(x-r, y+bkr, x-xkr, y+br, x, y+br) // bot l |
|
context.bezierCurveTo(x+xkr, y+br, x+r, y+bkr, x+r, y) // bot r |
|
context.closePath(); |
|
context.lineWidth = 1; |
|
context.fillStyle = 'rgb(255,' + Math.floor(255-r/rad*255) +',0)'; |
|
context.fill(); |
|
} |
|
|
|
// drow the blue surface |
|
var r = rad; |
|
tr =r*Math.cos(angle+Math.PI); // top hemisphere angle *rad |
|
br =r*Math.cos(angle+Math.PI); // bottom angle *rad |
|
if (angle<Math.PI) br = r; |
|
if (angle>Math.PI) tr = r; |
|
|
|
xkr = m*r; // kappa * r |
|
tkr = m*tr; // top height * kappa |
|
bkr = m*br; // bottom height * kappa |
|
|
|
context.beginPath() |
|
context.moveTo(x+r, y) |
|
context.bezierCurveTo(x+r, y-tkr, x+xkr, y-tr, x, y-tr) // top r |
|
context.bezierCurveTo(x-xkr, y-tr, x-r, y-tkr, x-r, y) // top l |
|
context.bezierCurveTo(x-r, y+bkr, x-xkr, y+br, x, y+br) // bot l |
|
context.bezierCurveTo(x+xkr, y+br, x+r, y+bkr, x+r, y) // bot r |
|
context.closePath(); |
|
context.lineWidth = 1; |
|
context.fillStyle = outerColor; |
|
context.fill(); |
|
context.restore(); |
|
} |
|
|
|
// movement functions |
|
|
|
function update() |
|
{ |
|
} |
|
|
|
// per frame tick functions |
|
|
|
function animate() |
|
{ |
|
frameCount++; |
|
// movement update |
|
update(); |
|
// render update |
|
render(); |
|
// trigger next frame |
|
requestAnimationFrame(animate); |
|
} |
|
|
|
|
|
// entry point |
|
init(); |
|
animate(); |