-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathspace-scene.js
219 lines (204 loc) · 9.5 KB
/
space-scene.js
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
function rand(min, max) {
return Math.random() * (max - min) + min;
}
function vec_dist(a, b) {
return Math.sqrt((a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2 + (a[2] - b[2]) ** 2);
}
let g_const = 1;
class myBall {
constructor(model, material, position = Vec.of(0, 0, 0), velocity = Vec.of(0, 0, 0), size = 1, mass = size ** 3) {
this.position = position;
this.velocity = velocity;
this.force = Vec.of(0, 0, 0);
this.size = size;
this.mass = mass;
this.sizeChange = Vec.of(0, 0, 0); // SizeChange is always negative.
this.material = material;
this.model = model;
}
update(dt) {
let a = this.force.times(1. / this.mass)
this.position = this.position.plus(this.velocity.times(dt))
.plus(a.times(0.5 * dt * dt));
this.velocity = this.velocity.plus(a.times(dt));
this.force = Vec.of(0,0,0);
}
draw(graphics_state) {
this.sizeVec = Vec.of(this.size, this.size, this.size);
let mat = Mat4.translation(this.position).times(
Mat4.scale(this.sizeVec.plus(this.sizeChange)));
this.model.draw(graphics_state, mat, this.material);
}
}
window.SpaceScene = window.classes.SpaceScene =
class SpaceScene extends Scene_Component {
constructor(context, control_box) // The scene begins by requesting the camera, shapes, and materials it will need.
{
super(context, control_box); // First, include a secondary Scene that provides movement controls:
if (!context.globals.has_controls)
context.register_scene_component(new Movement_Controls(context, control_box.parentElement.insertCell()));
context.globals.graphics_state.camera_transform = Mat4.look_at(Vec.of(0, 100, 100), Vec.of(0, 0, 0), Vec.of(0, 1, 0));
this.initial_camera_location = Mat4.inverse(context.globals.graphics_state.camera_transform);
const r = context.width / context.height;
context.globals.graphics_state.projection_transform = Mat4.perspective(Math.PI / 4, r, .1, 1000);
const shapes = {
sphere: new Subdivision_Sphere(4),
}
this.submit_shapes(context, shapes);
this.planets = [];
// Make some Material objects available to you:
this.materials =
{
test: context.get_instance(Phong_Shader).material(Color.of(1, 1, 0, 1), { ambient: .2 }),
sun: context.get_instance(Phong_Shader).material(Color.of(1, 1, 1, 1), { ambient: 0.9 }),
planet: context.get_instance(Phong_Shader).material(Color.of(0.7, 0.7, 0.7, 1), { ambient: 0.4, diffusivity: 1, specularity: 0 }),
}
this.planets.push(
new myBall(this.shapes.sphere, this.materials.sun.override({ color: Color.of(1, 0, 0, 1) }),
Vec.of(0,0,15),
Vec.of(5,0,0),
5,
100
),
new myBall(this.shapes.sphere, this.materials.sun.override({ color: Color.of(1, 0, 0, 1) }),
Vec.of(0, 0, -15),
Vec.of(-5, 0, 0),
5,
100
),
new myBall(this.shapes.sphere, this.materials.planet.override({ color: Color.of(0, 1, 0.5, 1) }),
Vec.of(20, 0, 0),
Vec.of(0, 20, 0),
1,
),
new myBall(this.shapes.sphere, this.materials.planet.override({ color: Color.of(0, 1, 0.5, 1) }),
Vec.of(-60, -20, 0),
Vec.of(0, 10, 0),
3,
),
new myBall(this.shapes.sphere, this.materials.planet.override({ color: Color.of(0, 1, 0.5, 1) }),
Vec.of(-56, -20, 0),
Vec.of(0, 15, 4),
0.5,
)
);
this.lights = [new Light(Vec.of(0, 0, 0, 1), Color.of(1, 1, 1, 1), 100000), new Light(Vec.of(-10, 20, 10, 1), Color.of(1, 1, 1, 1), 1000)];
}
make_control_panel() // Draw the scene's buttons, setup their actions and keyboard shortcuts, and monitor live measurements.
{
this.key_triggered_button("View solar system", ["0"], () => this.attached = () => this.initial_camera_location);
this.new_line();
this.key_triggered_button("Add planet", ["0"], () => {
this.planets.push(
new myBall(this.shapes.sphere,
this.materials.planet.override({ color: Color.of(rand(0, 0.5), rand(0.6, 1), rand(0.6, 1), 1) }),
Vec.of(rand(10, 30), rand(-20, 20), rand(-20, 20)),
Vec.of(0,0 , rand(-10, -30)),
rand(1, 3)
));
});
this.key_triggered_button("Add Random planet", ["1"], () => {
this.planets.push(
new myBall(this.shapes.sphere,
this.materials.planet.override({ color: Color.of(rand(0,0.5),rand(0.6,1),rand(0.6,1), 1) }),
Vec.of(rand(-50,50), rand(-10, 10), rand(-50, 50)),
Vec.of(rand(-10, 10), rand(-10, 10), rand(-10, 10)),
rand(1,3)
));
});
this.key_triggered_button("Add Star", ["2"], () => {
this.planets.push(
new myBall(this.shapes.sphere, this.materials.sun.override({ color: Color.of(1, 0, 0, 1) }),
Vec.of(rand(50,70), rand(-50, 50), rand(-50, 50)),
Vec.of(-this.planets[0].velocity[0]*2, rand(-15, 15), -this.planets[1].velocity[2]*2),
5,
100
))
});
this.new_line();
this.key_triggered_button("Decrease G", ["-"], () => {
g_const *= 0.5;
});
this.key_triggered_button("Increase G", ["+"], () => {
g_const *= 2;
});
}
display(graphics_state) {
graphics_state.lights = this.lights;
const t = graphics_state.animation_time / 1000, dt = graphics_state.animation_delta_time / 1000;
for (let i = 0; i < this.planets.length; i++) {
for (let j = i+1; j < this.planets.length; j++) {
const dist = vec_dist(this.planets[i].position, this.planets[j].position);
let magnitude = g_const * this.planets[i].mass * this.planets[j].mass / (dist ** 2);
magnitude = Math.min(magnitude, 100);
this.planets[i].force = this.planets[i].force.plus(this.planets[j].position.minus(this.planets[i].position).times(magnitude));
this.planets[j].force = this.planets[j].force.plus(this.planets[i].position.minus(this.planets[j].position).times(magnitude));
}
}
for (let i = 0; i < this.planets.length; i++) {
for (let j = i + 1; j < this.planets.length; j++) {
if (this.planets[i].to_del || this.planets[j].to_del)
continue;
const dist = vec_dist(this.planets[i].position,this.planets[j].position);
if (dist < Math.max(this.planets[i].size, this.planets[j].size)) {
this.planets[j].to_del = true;
this.planets[i].position = (this.planets[i].position.plus(this.planets[j].position)).times(1/2);
this.planets[i].velocity =
(this.planets[i].velocity.times(this.planets[i].mass)).plus(this.planets[j].velocity.times(this.planets[j].mass)).times(1/(this.planets[i].mass + this.planets[j].mass));
this.planets[i].size = Math.pow(this.planets[i].size ** 3 + this.planets[j].size ** 3, 1/3);
this.planets[i].mass += this.planets[j].mass;
}
}
}
this.planets = this.planets.filter(planet => !planet.to_del);
this.planets.forEach(planet => {
planet.update(dt);
planet.draw(graphics_state);
})
}
}
// Extra credit begins here (See TODO comments below):
window.Ring_Shader = window.classes.Ring_Shader =
class Ring_Shader extends Shader // Subclasses of Shader each store and manage a complete GPU program.
{
material() { return { shader: this } } // Materials here are minimal, without any settings.
map_attribute_name_to_buffer_name(name) // The shader will pull single entries out of the vertex arrays, by their data fields'
{ // names. Map those names onto the arrays we'll pull them from. This determines
// which kinds of Shapes this Shader is compatible with. Thanks to this function,
// Vertex buffers in the GPU can get their pointers matched up with pointers to
// attribute names in the GPU. Shapes and Shaders can still be compatible even
// if some vertex data feilds are unused.
return { object_space_pos: "positions" }[name]; // Use a simple lookup table.
}
// Define how to synchronize our JavaScript's variables to the GPU's:
update_GPU(g_state, model_transform, material, gpu = this.g_addrs, gl = this.gl) {
const proj_camera = g_state.projection_transform.times(g_state.camera_transform);
// Send our matrices to the shader programs:
gl.uniformMatrix4fv(gpu.model_transform_loc, false, Mat.flatten_2D_to_1D(model_transform.transposed()));
gl.uniformMatrix4fv(gpu.projection_camera_transform_loc, false, Mat.flatten_2D_to_1D(proj_camera.transposed()));
}
shared_glsl_code() // ********* SHARED CODE, INCLUDED IN BOTH SHADERS *********
{
return `precision mediump float;
varying vec4 position;
varying vec4 center;
`;
}
vertex_glsl_code() // ********* VERTEX SHADER *********
{
return `
attribute vec3 object_space_pos;
uniform mat4 model_transform;
uniform mat4 projection_camera_transform;
void main()
{
}`; // TODO: Complete the main function of the vertex shader (Extra Credit Part II).
}
fragment_glsl_code() // ********* FRAGMENT SHADER *********
{
return `
void main()
{
}`; // TODO: Complete the main function of the fragment shader (Extra Credit Part II).
}
}