+#include "blob_exhibit.h"
+#include "blobs/metasurf.h"
+#include "app.h"
+
+struct Metaball {
+ Vec3 pos;
+ float energy;
+ Vec3 path_scale, path_offset;
+ float phase_offset, speed;
+};
+#define NUM_MBALLS 8
+
+static Metaball def_mball_data[] = {
+ {Vec3(0, 0, 0), 2.18038, Vec3(1.09157, 1.69766, 1), Vec3(0.622818, 0.905624, 0), 1.24125, 0.835223},
+ {Vec3(0, 0, 0), 2.03646, Vec3(0.916662, 1.2161, 1), Vec3(0.118734, 0.283516, 0), 2.29201, 1.0134},
+ {Vec3(0, 0, 0), 2.40446, Vec3(1.87429, 1.57595, 1), Vec3(0.298566, -0.788474, 0), 3.8137, 0.516301},
+ {Vec3(0, 0, 0), 0.985774, Vec3(0.705847, 0.735019, 1), Vec3(0.669189, -0.217922, 0), 0.815497, 0.608809},
+ {Vec3(0, 0, 0), 2.49785, Vec3(0.827385, 1.75867, 1), Vec3(0.0284513, 0.247808, 0), 1.86002, 1.13755},
+ {Vec3(0, 0, 0), 1.54857, Vec3(1.24037, 0.938775, 1), Vec3(1.04011, 0.596987, 0), 3.30964, 1.26991},
+ {Vec3(0, 0, 0), 1.30046, Vec3(1.83729, 1.02869, 1), Vec3(-0.476708, 0.676994, 0), 5.77441, 0.569755},
+ {Vec3(0, 0, 0), 2.39865, Vec3(1.28899, 0.788321, 1), Vec3(-0.910677, 0.359099, 0), 5.5935, 0.848893}
+};
+
+struct BlobPriv {
+ metasurface *msurf;
+ Metaball mballs[NUM_MBALLS];
+ Texture *tex;
+};
+
+static void vertex(struct metasurface *ms, float x, float y, float z);
+static float eval(struct metasurface *ms, float x, float y, float z);
+
+
+BlobExhibit::BlobExhibit()
+{
+ priv = new BlobPriv;
+ for(int i=0; i<NUM_MBALLS; i++) {
+ priv->mballs[i] = def_mball_data[i];
+ }
+}
+
+BlobExhibit::~BlobExhibit()
+{
+ delete priv;
+}
+
+bool BlobExhibit::init()
+{
+ if(!(priv->msurf = msurf_create())) {
+ return false;
+ }
+ msurf_set_user_data(priv->msurf, priv);
+ msurf_set_threshold(priv->msurf, 8);
+ msurf_set_inside(priv->msurf, MSURF_GREATER);
+ msurf_set_bounds(priv->msurf, -3.5, 3.5, -3.5, 3.5, -3.5, 3.5);
+ msurf_eval_func(priv->msurf, eval);
+ msurf_vertex_func(priv->msurf, vertex);
+
+ priv->tex = texman.get_texture("data/sphmap.jpg");
+ return true;
+}
+
+void BlobExhibit::destroy()
+{
+ msurf_free(priv->msurf);
+ priv->msurf = 0;
+}
+
+void BlobExhibit::update(float dt)
+{
+ double sec = time_msec / 1000.0;
+
+ for(int i=0; i<NUM_MBALLS; i++) {
+ float t = fmod(sec * priv->mballs[i].speed + priv->mballs[i].phase_offset, M_PI * 2.0);
+ priv->mballs[i].pos.x = cos(t) * priv->mballs[i].path_scale.x + priv->mballs[i].path_offset.x;
+ priv->mballs[i].pos.y = sin(t) * priv->mballs[i].path_scale.y + priv->mballs[i].path_offset.y;
+ priv->mballs[i].pos.z = -cos(t) * priv->mballs[i].path_scale.z + priv->mballs[i].path_offset.z;
+ }
+}
+
+void BlobExhibit::draw() const
+{
+ pre_draw();
+
+ glPushAttrib(GL_ENABLE_BIT);
+
+ glUseProgram(0);
+
+ glDisable(GL_LIGHTING);
+ glEnable(GL_TEXTURE_2D);
+ priv->tex->bind();
+
+ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
+ glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
+ glEnable(GL_TEXTURE_GEN_S);
+ glEnable(GL_TEXTURE_GEN_T);
+
+ glFrontFace(GL_CW);
+ glBegin(GL_TRIANGLES);
+ glColor3f(1, 1, 1);
+ msurf_polygonize(priv->msurf);
+ glEnd();
+ glFrontFace(GL_CCW);
+
+ glPopAttrib();
+
+ post_draw();
+}
+
+static void vertex(struct metasurface *ms, float x, float y, float z)
+{
+ static const float delta = 0.01;
+
+ float val = eval(ms, x, y, z);
+ float dfdx = eval(ms, x + delta, y, z) - val;
+ float dfdy = eval(ms, x, y + delta, z) - val;
+ float dfdz = eval(ms, x, y, z + delta) - val;
+
+ glNormal3f(dfdx, dfdy, dfdz);
+ glVertex3f(x, y, z);
+}
+
+static float eval(struct metasurface *ms, float x, float y, float z)
+{
+ float sum = 0.0f;
+ BlobPriv *priv = (BlobPriv*)msurf_get_user_data(ms);
+
+ for(int i=0; i<NUM_MBALLS; i++) {
+ float dx = x - priv->mballs[i].pos.x;
+ float dy = y - priv->mballs[i].pos.y;
+ float dz = z - priv->mballs[i].pos.z;
+ float dsq = dx * dx + dy * dy + dz * dz;
+
+ sum += priv->mballs[i].energy / dsq;
+ }
+ return sum;
+}