Skip to content

Commit a9e44a3

Browse files
committed
emscripten
1 parent 08036e7 commit a9e44a3

17 files changed

+464
-348
lines changed

agents.c

+346
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,346 @@
1+
// Use ./compile.sh to compile this file
2+
// See comments there for further information...
3+
4+
//#include <stdio.h>
5+
#include <stdint.h>
6+
#include <stdlib.h>
7+
#include <math.h>
8+
9+
#ifdef __EMSCRIPTEN__
10+
#include <emscripten.h>
11+
#else
12+
#define EMSCRIPTEN_KEEPALIVE
13+
#endif
14+
15+
#define MIN(X,Y) ((X) < (Y) ? (X) : (Y))
16+
17+
typedef struct {
18+
float x,y,z;
19+
} Vec3;
20+
21+
typedef struct {
22+
Vec3 pos; // 12 bytes
23+
Vec3 vel; // 12 bytes
24+
Vec3 acc; // 12 bytes
25+
float dist; // 4 bytes
26+
} Agent; // 40 bytes
27+
28+
typedef struct {
29+
Agent *agents; // 4 bytes
30+
uint32_t agent_count; // 4
31+
float speed; // 4
32+
float cohesion; // 4
33+
float separation; // 4
34+
float alignment; // 4
35+
float radius; // 4
36+
Vec3 size; // 12
37+
} AgentSystem;
38+
39+
40+
static inline void setVec3(Vec3 *v, float x, float y, float z) {
41+
v->x = x;
42+
v->y = y;
43+
v->z = z;
44+
}
45+
46+
47+
static inline void copyVec3(Vec3* v, Vec3* v2) {
48+
v->x = v2->x;
49+
v->y = v2->y;
50+
v->z = v2->z;
51+
}
52+
53+
54+
55+
static inline void addVec3(Vec3* v, Vec3* v2) {
56+
v->x += v2->x;
57+
v->y += v2->y;
58+
v->z += v2->z;
59+
}
60+
61+
62+
static inline void subVec3(Vec3* v, Vec3* v2) {
63+
v->x -= v2->x;
64+
v->y -= v2->y;
65+
v->z -= v2->z;
66+
}
67+
68+
69+
static inline void scaleVec3(Vec3* v, float s) {
70+
v->x *= s;
71+
v->y *= s;
72+
v->z *= s;
73+
}
74+
75+
76+
static inline void normalizeVec3(Vec3* v, float l) {
77+
float m = sqrtf(v->x * v->x + v->y * v->y + v->z * v->z);
78+
if (m > 0.0f) {
79+
l /= m;
80+
v->x *= l;
81+
v->y *= l;
82+
v->z *= l;
83+
}
84+
}
85+
86+
static inline float distVec3(Vec3 *v, Vec3 *v2) {
87+
float dx = v->x - v2->x;
88+
float dy = v->y - v2->y;
89+
float dz = v->z - v2->z;
90+
return sqrtf(dx * dx + dy * dy + dz * dz);
91+
}
92+
93+
static inline float randf01() {
94+
return (float)rand() / (float)RAND_MAX;
95+
}
96+
97+
98+
static inline float randf() {
99+
return randf01() * 2.0f - 1.0f;
100+
}
101+
102+
103+
static Agent* generate_agents(Vec3 size, float speed, uint32_t agent_count) {
104+
Agent *agents = (Agent*)malloc(agent_count * sizeof(Agent));
105+
Agent *a;
106+
107+
for(uint32_t i=0; i < agent_count; i++) {
108+
a = &agents[i];
109+
setVec3(&a->pos, randf()*size.x, randf()*size.y, randf()*size.z);
110+
setVec3(&a->vel, randf()*speed, randf()*speed, randf()*speed);
111+
setVec3(&a->acc, 0, 0, 0);
112+
a->dist = 0;
113+
}
114+
115+
return agents;
116+
}
117+
118+
119+
120+
static inline void limitVec3(Vec3 *v, float l) {
121+
float *v2 = (float *)v;
122+
float ratio = 0.0f;
123+
for(uint32_t i = 0; i<3; i++ ) {
124+
if(v2[i] > l) {
125+
ratio = l / v2[i];
126+
for(uint32_t j = 0; j<3; j++ ) {
127+
v2[j] *= ratio;
128+
}
129+
}
130+
}
131+
}
132+
133+
134+
static void move(AgentSystem *sys) {
135+
Agent *a;
136+
Vec3 force;
137+
138+
for(uint32_t i=0; i < sys->agent_count; i++) {
139+
a = &sys->agents[i];
140+
copyVec3(&force, &a->vel);
141+
addVec3(&force, &a->acc);
142+
limitVec3(&force, sys->speed);
143+
copyVec3(&a->vel,&force);
144+
addVec3(&a->pos, &a->vel);
145+
setVec3(&a->acc,0,0,0);
146+
}
147+
}
148+
149+
150+
151+
static void bounce(AgentSystem *sys) {
152+
Agent *a;
153+
154+
for(uint32_t i=0; i < sys->agent_count; i++) {
155+
a = &sys->agents[i];
156+
157+
if((a->pos.x < 0) || (a->pos.x > sys->size.x)) {
158+
a->vel.x *= -1.0f;
159+
}
160+
if((a->pos.y < 0) || (a->pos.y > sys->size.y)) {
161+
a->vel.y *= -1.0f;
162+
}
163+
if((a->pos.z < 0) || (a->pos.z > sys->size.z)) {
164+
a->vel.z *= -1.0f;
165+
}
166+
}
167+
}
168+
169+
170+
171+
static void swarm_separate(AgentSystem *sys, Agent *a, Vec3 *result) {
172+
Vec3 tmp, force;
173+
Agent *other;
174+
175+
setVec3(&force,0,0,0);
176+
177+
for(uint32_t i=0; i < sys->agent_count; i++) {
178+
other = &sys->agents[i];
179+
180+
if(other->dist) {
181+
copyVec3(&tmp, &a->pos);
182+
subVec3(&tmp, &other->pos);
183+
scaleVec3(&tmp, sys->radius / other->dist);
184+
addVec3(&force, &tmp);
185+
}
186+
}
187+
188+
normalizeVec3(&force, 1.0f);
189+
scaleVec3(&force, sys->separation);
190+
addVec3(result, &force);
191+
}
192+
193+
194+
195+
static void swarm_align(AgentSystem *sys, Agent *a, Vec3 *result) {
196+
Vec3 tmp, force;
197+
Agent *other;
198+
uint32_t near = 0;
199+
200+
setVec3(&force,0,0,0);
201+
202+
for(uint32_t i=0; i < sys->agent_count; i++) {
203+
other = &sys->agents[i];
204+
205+
if(other->dist) {
206+
copyVec3(&tmp, &other->vel);
207+
scaleVec3(&tmp, sys->radius / other->dist);
208+
addVec3(&force, &tmp);
209+
near++;
210+
}
211+
}
212+
213+
if(near) {
214+
scaleVec3(&force, 1.0f / near);
215+
normalizeVec3(&force, 1.0f);
216+
scaleVec3(&force, sys->alignment);
217+
addVec3(result, &force);
218+
}
219+
}
220+
221+
222+
223+
static void swarm_cohere(AgentSystem *sys, Agent *a, Vec3 *result) {
224+
Vec3 tmp, force;
225+
Agent *other;
226+
uint32_t near = 0;
227+
float dist;
228+
229+
setVec3(&force,0,0,0);
230+
231+
for(uint32_t i=0; i < sys->agent_count; i++) {
232+
other = &sys->agents[i];
233+
if(other->dist) {
234+
addVec3(&force, &other->pos);
235+
near++;
236+
}
237+
}
238+
239+
if(near) {
240+
scaleVec3(&force, 1.0f / near);
241+
dist = distVec3(&a->pos, &force);
242+
if(dist) {
243+
subVec3(&force, &a->pos);
244+
scaleVec3(&force, sys->radius / dist);
245+
normalizeVec3(&force, 1.0f);
246+
scaleVec3(&force, sys->cohesion);
247+
addVec3(result, &force);
248+
}
249+
}
250+
}
251+
252+
253+
static int swarm_distance(AgentSystem *sys, Agent *a) {
254+
Agent *other;
255+
int count = 0;
256+
float dist;
257+
258+
for(uint32_t i=0; i < sys->agent_count; i++) {
259+
other = &sys->agents[i];
260+
other->dist = 0;
261+
dist = distVec3(&a->pos, &other->pos);
262+
if(dist && (dist < sys->radius)) {
263+
other->dist = dist;
264+
count++;
265+
}
266+
}
267+
return count;
268+
}
269+
270+
271+
static void swarm(AgentSystem *sys) {
272+
Vec3 force;
273+
Agent *a;
274+
275+
for(uint32_t i=0; i < sys->agent_count; i++) {
276+
a = &sys->agents[i];
277+
278+
if(swarm_distance(sys,a)) {
279+
setVec3(&force,0,0,0);
280+
swarm_separate(sys, a, &force);
281+
swarm_align(sys, a, &force);
282+
swarm_cohere(sys, a, &force);
283+
addVec3(&a->acc, &force);
284+
}
285+
}
286+
}
287+
288+
289+
uint32_t get_agent_count(AgentSystem* sys) {
290+
return sys->agent_count;
291+
}
292+
293+
Agent* get_agents_pointer(AgentSystem* sys) {
294+
return sys->agents;
295+
}
296+
297+
298+
float get_agent_component(AgentSystem* sys, uint32_t idx, uint32_t component) {
299+
float *pos = (float*)&(sys->agents[idx]).pos;
300+
return pos[component];
301+
}
302+
303+
304+
EMSCRIPTEN_KEEPALIVE AgentSystem* update_agent_config(AgentSystem *sys, float x, float y, float z, uint32_t count, float cohesion,
305+
float separation, float alignment, float speed, float radius) {
306+
307+
setVec3(&sys->size, x, y, z);
308+
sys->agent_count = count;
309+
sys->separation = separation;
310+
sys->cohesion = cohesion;
311+
sys->alignment = alignment;
312+
sys->speed = speed;
313+
sys->radius = radius;
314+
315+
return sys;
316+
}
317+
318+
319+
EMSCRIPTEN_KEEPALIVE AgentSystem* init_agent_system(float x, float y, float z, uint32_t count, float cohesion,
320+
float separation, float alignment, float speed, float radius) {
321+
AgentSystem *sys = (AgentSystem*)malloc(sizeof(AgentSystem));
322+
323+
update_agent_config(sys, x, y, z, count, cohesion, separation, alignment, speed, radius);
324+
sys->agents = generate_agents(sys->size, sys->speed, sys->agent_count);
325+
326+
return sys;
327+
}
328+
329+
330+
331+
EMSCRIPTEN_KEEPALIVE AgentSystem* update_agent_system(AgentSystem* sys) {
332+
333+
swarm(sys);
334+
move(sys);
335+
bounce(sys);
336+
337+
return sys;
338+
}
339+
340+
/*
341+
int main() {
342+
char *foo = "Hello Emscripten!";
343+
printf("%s\n", foo);
344+
return 0;
345+
}
346+
*/

build.boot

+7-3
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,13 @@
3232

3333

3434
(task-options! cljs {:compiler-options
35-
{:output-to "js/main.js"
35+
{:output-to "js/art.js"
3636
:output-dir "js/out"
3737
:asset-path "js/out"
3838
:parallel-build true
3939
:main 'art.core
4040
;;:pseudo-names true
41-
}})
41+
:externs ["externs.js"]}})
4242

4343

4444

@@ -69,7 +69,11 @@
6969
(task-options! cljs {:optimizations :advanced})
7070
(comp
7171
(cljs)
72-
(sift :include #{#"js/.*js$" #"index.html$" #"css/.*css$" #"svg/.*svg$" #"fonts/.*"})
72+
(sift :include #{#"js/.*js$"
73+
#"index.html$"
74+
#"css/.*css$"
75+
#"svg/.*svg$"
76+
#"fonts/.*"})
7377
(target :dir #{"release-js"})))
7478

7579

compile.sh

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#!/bin/sh
2+
3+
# This compiles the file agents.c with optimizations,
4+
# declares a number of exported function names,
5+
# post-processes the resulting JS with Closure compiler and
6+
# wraps all within a global 'Agents' module/object
7+
# This module MUST be initialized by calling 'Agents();'
8+
9+
emcc -O2 -s ASM_JS=1 -s INVOKE_RUN=0 \
10+
-s EXPORTED_FUNCTIONS="['_main','_init_agent_system','_update_agent_system','_get_agent_count','_get_agent_component','_get_agents_pointer','_update_agent_config']" \
11+
-s EXTRA_EXPORTED_RUNTIME_METHODS='["ccall", "cwrap"]' \
12+
-s "EXPORT_NAME='Agents'" \
13+
-s MODULARIZE=1 \
14+
--memory-init-file 0 \
15+
--closure 1 \
16+
-o resources/js/agents.js \
17+
agents.c
18+

externs.js

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
var Module = {};
2+
3+
Module.ccall = function() {};
4+
Module.cwrap = function() {};

0 commit comments

Comments
 (0)