Skip to content

Commit ba9b05e

Browse files
committed
code skeleton
0 parents  commit ba9b05e

23 files changed

+4210
-0
lines changed

.gitattributes

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Auto detect text files and perform LF normalization
2+
* text=auto

.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
*.gif
2+
*.ico
3+
*.png
4+
*.jpg
5+
*.obj
6+
*.bat
7+
*.command
8+
.vscode/*

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
You can write your README here

examples/axes-viewer.js

Lines changed: 425 additions & 0 deletions
Large diffs are not rendered by default.

examples/collisions-demo.js

Lines changed: 317 additions & 0 deletions
Large diffs are not rendered by default.

examples/common-components.js

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
import {tiny} from '../tiny-graphics.js';
2+
// Pull these names into this module's scope for convenience:
3+
const {Vector, Vector3, vec, vec3, vec4, color, Matrix, Mat4, Shape, Shader, Component} = tiny;
4+
5+
const defs = {};
6+
7+
export {tiny, defs};
8+
9+
const Minimal_Webgl_Demo = defs.Minimal_Webgl_Demo =
10+
class Minimal_Webgl_Demo extends Component {
11+
init () {
12+
this.widget_options = {make_controls: false}; // This demo is too minimal to have controls
13+
this.shapes = {triangle: new Minimal_Shape ()};
14+
this.shader = new Basic_Shader ();
15+
}
16+
render_animation (caller) {
17+
this.shapes.triangle.draw (caller, this.uniforms, Mat4.identity (), {shader: this.shader});
18+
}
19+
};
20+
21+
const Movement_Controls = defs.Movement_Controls =
22+
class Movement_Controls extends Component {
23+
roll = 0;
24+
look_around_locked = true;
25+
thrust = vec3 (0, 0, 0);
26+
pos = vec3 (0, 0, 0);
27+
z_axis = vec3 (0, 0, 0);
28+
radians_per_frame = 1 / 200;
29+
meters_per_frame = 20;
30+
speed_multiplier = 1;
31+
mouse_enabled_canvases = new Set ();
32+
will_take_over_uniforms = true;
33+
set_recipient (matrix_closure, inverse_closure) {
34+
this.matrix = matrix_closure;
35+
this.inverse = inverse_closure;
36+
}
37+
reset () {
38+
this.set_recipient (() => this.uniforms.camera_transform,
39+
() => this.uniforms.camera_inverse);
40+
}
41+
add_mouse_controls (canvas) {
42+
if (this.mouse_enabled_canvases.has (canvas))
43+
return;
44+
this.mouse_enabled_canvases.add (canvas);
45+
// First, measure mouse steering, for rotating the flyaround camera:
46+
this.mouse = {"from_center": vec (0, 0)};
47+
const mouse_position = (e, rect = canvas.getBoundingClientRect ()) =>
48+
vec (e.clientX - (rect.left + rect.right) / 2, e.clientY - (rect.bottom + rect.top) / 2);
49+
// Set up mouse response. The last one stops us from reacting if the mouse leaves the canvas:
50+
document.addEventListener ("mouseup", e => { this.mouse.anchor = undefined; });
51+
canvas.addEventListener ("mousedown", e => {
52+
e.preventDefault ();
53+
this.mouse.anchor = mouse_position (e);
54+
});
55+
canvas.addEventListener ("mousemove", e => {
56+
e.preventDefault ();
57+
this.mouse.from_center = mouse_position (e);
58+
});
59+
canvas.addEventListener ("mouseout", e => { if ( !this.mouse.anchor) this.mouse.from_center.scale_by (0); });
60+
}
61+
render_explanation (document_builder, document_element = document_builder.document_region) { }
62+
render_controls () {
63+
this.control_panel.innerHTML += "Click and drag the scene to <br> spin your viewpoint around it.<br>";
64+
this.key_triggered_button ("Up", [" "], () => this.thrust[ 1 ] = -1, undefined, () => this.thrust[ 1 ] = 0);
65+
this.key_triggered_button ("Forward", ["w"], () => this.thrust[ 2 ] = 1, undefined,
66+
() => this.thrust[ 2 ] = 0);
67+
this.new_line ();
68+
this.key_triggered_button ("Left", ["a"], () => this.thrust[ 0 ] = 1, undefined, () => this.thrust[ 0 ] = 0);
69+
this.key_triggered_button ("Back", ["s"], () => this.thrust[ 2 ] = -1, undefined, () => this.thrust[ 2 ] = 0);
70+
this.key_triggered_button ("Right", ["d"], () => this.thrust[ 0 ] = -1, undefined,
71+
() => this.thrust[ 0 ] = 0);
72+
this.new_line ();
73+
this.key_triggered_button ("Down", ["z"], () => this.thrust[ 1 ] = 1, undefined, () => this.thrust[ 1 ] = 0);
74+
75+
const speed_controls = this.control_panel.appendChild (document.createElement ("span"));
76+
speed_controls.style.margin = "30px";
77+
this.key_triggered_button ("-", ["o"], () =>
78+
this.speed_multiplier /= 1.2, "green", undefined, undefined, speed_controls);
79+
this.live_string (box => { box.textContent = "Speed: " + this.speed_multiplier.toFixed (2); },
80+
speed_controls);
81+
this.key_triggered_button ("+", ["p"], () =>
82+
this.speed_multiplier *= 1.2, "green", undefined, undefined, speed_controls);
83+
this.new_line ();
84+
this.key_triggered_button ("Roll left", [","], () => this.roll = 1, undefined, () => this.roll = 0);
85+
this.key_triggered_button ("Roll right", ["."], () => this.roll = -1, undefined, () => this.roll = 0);
86+
this.new_line ();
87+
this.key_triggered_button ("(Un)freeze mouse look around", ["f"], () => this.look_around_locked ^= 1,
88+
"green");
89+
this.new_line ();
90+
this.live_string (
91+
box => box.textContent = "Position: " + this.pos[ 0 ].toFixed (2) + ", " + this.pos[ 1 ].toFixed (2)
92+
+ ", " + this.pos[ 2 ].toFixed (2));
93+
this.new_line ();
94+
// The facing directions are surprisingly affected by the left hand rule:
95+
this.live_string (box => box.textContent = "Facing: " + ((this.z_axis[ 0 ] > 0 ? "West " : "East ")
96+
+ (this.z_axis[ 1 ] > 0 ? "Down " : "Up ") +
97+
(this.z_axis[ 2 ] > 0 ? "North" : "South")));
98+
this.new_line ();
99+
this.key_triggered_button ("Go to world origin", ["r"], () => {
100+
this.matrix ().set_identity (4, 4);
101+
this.inverse ().set_identity (4, 4);
102+
}, "orange");
103+
this.new_line ();
104+
105+
this.key_triggered_button ("Look at origin from front", ["1"], () => {
106+
this.inverse ().set (Mat4.look_at (vec3 (0, 0, 10), vec3 (0, 0, 0), vec3 (0, 1, 0)));
107+
this.matrix ().set (Mat4.inverse (this.inverse ()));
108+
}, "black");
109+
this.new_line ();
110+
this.key_triggered_button ("from right", ["2"], () => {
111+
this.inverse ().set (Mat4.look_at (vec3 (10, 0, 0), vec3 (0, 0, 0), vec3 (0, 1, 0)));
112+
this.matrix ().set (Mat4.inverse (this.inverse ()));
113+
}, "black");
114+
this.key_triggered_button ("from rear", ["3"], () => {
115+
this.inverse ().set (Mat4.look_at (vec3 (0, 0, -10), vec3 (0, 0, 0), vec3 (0, 1, 0)));
116+
this.matrix ().set (Mat4.inverse (this.inverse ()));
117+
}, "black");
118+
this.key_triggered_button ("from left", ["4"], () => {
119+
this.inverse ().set (Mat4.look_at (vec3 (-10, 0, 0), vec3 (0, 0, 0), vec3 (0, 1, 0)));
120+
this.matrix ().set (Mat4.inverse (this.inverse ()));
121+
}, "black");
122+
this.new_line ();
123+
this.key_triggered_button ("Attach to global camera", ["Shift", "R"],
124+
() => { this.will_take_over_uniforms = true; }, "blue");
125+
this.new_line ();
126+
}
127+
first_person_flyaround (radians_per_frame, meters_per_frame, leeway = 70) {
128+
// Compare mouse's location to all four corners of a dead box:
129+
const offsets_from_dead_box = {
130+
plus : [this.mouse.from_center[ 0 ] + leeway, this.mouse.from_center[ 1 ] + leeway],
131+
minus: [this.mouse.from_center[ 0 ] - leeway, this.mouse.from_center[ 1 ] - leeway]
132+
};
133+
// Apply a camera rotation movement, but only when the mouse is
134+
// past a minimum distance (leeway) from the canvas's center:
135+
if ( !this.look_around_locked)
136+
// If steering, steer according to "mouse_from_center" vector, but don't
137+
// start increasing until outside a leeway window from the center.
138+
for (let i = 0; i < 2; i++) {
139+
// The &&'s in the next line might zero the vectors out:
140+
let o = offsets_from_dead_box,
141+
velocity = ((o.minus[ i ] > 0 && o.minus[ i ]) || (o.plus[ i ] < 0 && o.plus[ i ])) *
142+
radians_per_frame;
143+
// On X step, rotate around Y axis, and vice versa.
144+
this.matrix ().post_multiply (Mat4.rotation (-velocity, i, 1 - i, 0));
145+
this.inverse ().pre_multiply (Mat4.rotation (+velocity, i, 1 - i, 0));
146+
}
147+
this.matrix ().post_multiply (Mat4.rotation (-.1 * this.roll, 0, 0, 1));
148+
this.inverse ().pre_multiply (Mat4.rotation (+.1 * this.roll, 0, 0, 1));
149+
// Now apply translation movement of the camera, in the newest local coordinate frame.
150+
this.matrix ().post_multiply (Mat4.translation (...this.thrust.times (-meters_per_frame)));
151+
this.inverse ().pre_multiply (Mat4.translation (...this.thrust.times (+meters_per_frame)));
152+
}
153+
third_person_arcball (radians_per_frame) {
154+
// Spin the scene around a point on an axis determined by user mouse drag:
155+
const dragging_vector = this.mouse.from_center.minus (this.mouse.anchor);
156+
if (dragging_vector.norm () <= 0)
157+
return;
158+
this.matrix ().post_multiply (Mat4.translation (0, 0, -25));
159+
this.inverse ().pre_multiply (Mat4.translation (0, 0, +25));
160+
161+
const rotation = Mat4.rotation (radians_per_frame * dragging_vector.norm (),
162+
dragging_vector[ 1 ], dragging_vector[ 0 ], 0);
163+
this.matrix ().post_multiply (rotation);
164+
this.inverse ().pre_multiply (rotation);
165+
166+
this.matrix ().post_multiply (Mat4.translation (0, 0, +25));
167+
this.inverse ().pre_multiply (Mat4.translation (0, 0, -25));
168+
}
169+
render_animation (context) {
170+
const m = this.speed_multiplier * this.meters_per_frame,
171+
r = this.speed_multiplier * this.radians_per_frame,
172+
dt = this.uniforms.animation_delta_time / 1000;
173+
174+
// TODO: Once there is a way to test it, remove the below, because uniforms are no longer inaccessible
175+
// outside this function, so we could just tell this class to take over the uniforms' matrix anytime.
176+
if (this.will_take_over_uniforms) {
177+
this.reset ();
178+
this.will_take_over_uniforms = false;
179+
}
180+
// Move in first-person. Scale the normal camera aiming speed by dt for smoothness:
181+
this.first_person_flyaround (dt * r, dt * m);
182+
// Also apply third-person "arcball" camera mode if a mouse drag is occurring:
183+
if (this.mouse.anchor)
184+
this.third_person_arcball (dt * r);
185+
// Log some values:
186+
this.pos = this.inverse ().times (vec4 (0, 0, 0, 1));
187+
this.z_axis = this.inverse ().times (vec4 (0, 0, 1, 0));
188+
}
189+
};

0 commit comments

Comments
 (0)