aboutsummaryrefslogtreecommitdiff
path: root/src/index.ts
blob: 3736c92bd3ac7e181f9314479b8b20fcb8d11d8e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import {Colour} from './Colour';
import {Framebuffer} from './Framebuffer';
import {Plane, Sphere} from './Geometry';
import {Light} from './Light';
import {Material} from './Material';
import {RaytraceDispatcher} from './RaytraceDispatcher';
import {RaytraceContext, RaytracerOptions} from './RaytraceContext';
import {Vector} from './Vector';
import {Logger} from './Logger';

function render() {
  getRenderButton().classList.add('loading');
  const dispatcher = initDispatcher(parseOptions());
  dispatcher.requestRender();
}

function initDispatcher(options: RaytracerOptions): RaytraceDispatcher {
  const width = 960;
  const height = 720;
  const fov = Math.PI / 3;

  const framebuffer = new Framebuffer(width, height);

  const matWhite = new Material(new Colour(102, 102, 77), 0.6, 0.3, 0.1, 50);
  const matRed = new Material(new Colour(77, 26, 26), 0.9, 0.1, 0.0, 10);
  const matMirror = new Material(new Colour(193, 193, 193), 0.0, 10, 0.8, 1000);
  const matGreen = new Material(new Colour(77, 255, 26), 0.3, 0.1, 0.0, 2);

  const spheres = [
    new Sphere(2, new Vector(-3, 0, -16), matWhite),
    new Sphere(2, new Vector(-1, -1.5, -12), matGreen),
    new Sphere(3, new Vector(1.5, -0.5, -18), matRed),
    new Sphere(4, new Vector(7, 5, -18), matMirror),
  ];

  const planes = [new Plane(-4, 10, -10, -30, 0.7)];

  const lights = [
    new Light(new Vector(-20, 20, 20), 1.5),
    new Light(new Vector(30, 50, -25), 1.8),
    new Light(new Vector(30, 20, 30), 1.7),
  ];

  const context = new RaytraceContext(
    height,
    width,
    fov,
    spheres,
    planes,
    lights,
    options
  );

  return new RaytraceDispatcher(
    framebuffer,
    context,
    new Logger(),
    onRenderComplete
  );
}

function parseOptions(): RaytracerOptions {
  return {
    numThreads: getDesiredThreadCount(),
    shadows: getInputElement('shadows-toggle').checked,
    diffuseLighting: getInputElement('diffuse-toggle').checked,
    specularLighting: getInputElement('specular-toggle').checked,
    reflections: getInputElement('reflections-toggle').checked,
    maxRecurseDepth: 5,
    maxDrawDistance: 1000,
  };
}

function registerEventListeners() {
  getRenderButton().addEventListener('click', render);

  const threadsSlider = getInputElement('threads');
  threadsSlider.addEventListener('input', () => {
    getInputElement('threads-value').textContent = threadsSlider.value;
  });

  const threadToggle = getInputElement('enable-threads-toggle');
  threadToggle.addEventListener('change', () => {
    threadsSlider.disabled = !threadToggle.checked;
  });
}

function getDesiredThreadCount(): number {
  if (getInputElement('enable-threads-toggle').checked) {
    return Number.parseInt(getInputElement('threads').value);
  } else {
    return 1;
  }
}

function onRenderComplete() {
  getRenderButton().classList.remove('loading');
}

function getRenderButton(): HTMLElement {
  return document.getElementById('render')!;
}

function getInputElement(elementId: string) {
  return document.getElementById(elementId) as HTMLInputElement;
}

document.addEventListener('DOMContentLoaded', () => {
  registerEventListeners();
  render();
});