LogoLogo
  • Home
  • Projects
  • About
  • Contact

A Little Metaball Action

Devon O. · October 02, 2009 · Actionscript · 3 comments
3

And now for something completely different…

The other day, for reasons I can’t even explain to myself, I googled for metaball examples. No real need, I just rather like the way they look, all blobby and stuff. Well, one of the first things to come up was this nifty example in Java by Gordon Williams. I promptly decompiled the java and ported it all over to Actionscript 3. Afterwards I emailed the author and asked if it would be all right to publish the AS source, and he kindly said it would be fine (I know, I know, it’s a bit of an ass backward way to operate, but if he hadn’t been kind enough to say it was all right, you wouldn’t be reading this now).

So this is the result I got (double click the thing to get it going or stop it):

Get Adobe Flash player

As you can, it’s currently (at least on my machine) running at around a craptacular 11 frames per second. If anyone would like to try their hand at optimizing the sucker please have at the code below (and please post your results here – I’d like to see what people come up with).

A couple supporting classes :

Vect.as

C#
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
package {
    
    /**
     * @author Gordon Williams (http://www.rabidhamster.org/index.php)
     *
     * Java to Actionscript port by:
     * @author Devon O. Wolfgang (https://blog.onebyonedesign.com)
     */
 
    public class Vect {
        
        public var d:Vector.<Number> = new Vector.<Number>();
 
        public function Vect(paramFloat1:Number, paramFloat2:Number, paramFloat3:Number) {
            d.length = 4;
            d.fixed = true;
            d[0] = paramFloat1;
            d[1] = paramFloat2;
            d[2] = paramFloat3;
            d[3] = 1.0;
        }
 
        public function magnitude():Number {
            return Math.sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]);
        }
 
        public function equal(paramVect:Vect):Boolean {
            return ((paramVect.d[0] == d[0]) && (paramVect.d[1] == this.d[1]) && (paramVect.d[2] == this.d[2]));
        }
 
        public function mul(paramFloat:Number):void {
            d[0] *= paramFloat;
            d[1] *= paramFloat;
            d[2] *= paramFloat;
        }
 
        public function div(paramFloat:Number):void {
            d[0] /= paramFloat;
            d[1] /= paramFloat;
            d[2] /= paramFloat;
        }
 
        public function add(paramVect:Vect):void {
            d[0] += paramVect.d[0];
            d[1] += paramVect.d[1];
            d[2] += paramVect.d[2];
        }
 
        public function sub(paramVect:Vect):void {
            d[0] -= paramVect.d[0];
            d[1] -= paramVect.d[1];
            d[2] -= paramVect.d[2];
        }
 
        public function dot(paramVect:Vect):Number {
            return (d[0] * paramVect.d[0] + d[1] * paramVect.d[1] + d[2] * paramVect.d[2]);
        }
 
        public function cross(paramVect:Vect):Vect {
            return new Vect(d[1] * paramVect.d[2] - (d[2] * paramVect.d[1]), d[2] * paramVect.d[0] - (d[0] * paramVect.d[2]), d[0] * paramVect.d[1] - (d[1] * paramVect.d[0]));
        }
 
        public function normalize():void {
            mul(1.0 / magnitude());
        }
 
        public function project():void {
            d[2] += 256.0;
            d[0] *= 1024.0 / d[2];
            d[1] *= 1024.0 / d[2];
        }
 
        public function copy():Vect {
            return new Vect(d[0], d[1], d[2]);
        }
    }
}

Matrix.as

C#
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
package {
    
    /**
     * @author Gordon Williams (http://www.rabidhamster.org/index.php)
     *
     * Java to Actionscript port by:
     * @author Devon O. Wolfgang (https://blog.onebyonedesign.com)
     */
    
    public class Matrix {
        
        public var m:Vector.<Vector.<Number>> = new Vector.<Vector.<Number>>();
        
        public function Matrix() {
            m.length = 4;
            m.fixed = true;
            var i:int = 4;
            while (i--) {
                var v:Vector.<Number> = new Vector.<Number>();
                v.length = 4;
                v.fixed = true;
                m[i] = v;
            }
            setZero();
        }
 
        public function setZero():void {
            for (var i:int = 0; i < 4; ++i) {
                for (var j:int = 0; j < 4; ++j ) {
                    this.m[i][j] = 0.0;
                }
            }
        }
 
        public function setIdentity():void {
            setZero();
            m[0][0] = 1.0;
            m[1][1] = 1.0;
            m[2][2] = 1.0;
            m[3][3] = 1.0;
        }
 
        public function setTranslate(...args:Array):void {
            if (args.length == 1) {
                setTranslate(args[0].d[0], args[0].d[1], args[0].d[2]);
            } else if (args.length == 3) {
                setIdentity();
                m[3][0] = args[0];
                m[3][1] = args[1];
                m[3][2] = args[2];
            }
        }
 
        public function setRotateX(paramFloat:Number):void  {
            setIdentity();
            m[2][2] = (m[1][1] = Math.cos(paramFloat));
            m[2][1] = (-(m[1][2] = Math.sin(paramFloat)));
        }
 
        public function setRotateY(paramFloat:Number):void {
            setIdentity();
            m[2][2] = (m[0][0] = Math.cos(paramFloat));
            m[0][2] = (-(m[2][0] = Math.sin(paramFloat)));
        }
 
        public function setRotateZ(paramFloat:Number):void {
            setIdentity();
            m[0][0] = (m[1][1] = Math.cos(paramFloat));
            m[1][0] = (-(m[0][1] = Math.sin(paramFloat)));
        }
 
        public function setScale(...args:Array):void {
            if (args.length == 1) {
                setScale(args[0].d[0], args[0].paramVect.d[1], args[0].paramVect.d[2]);
            } else if (args.length == 3) {
                setIdentity();
                m[0][0] = args[0];
                m[1][1] = args[1];
                m[2][2] = args[2];
            }
        }
 
        public function mul(paramMatrix:Matrix):Matrix {
            var localMatrix:Matrix = new Matrix();
            for (var i:int = 0; i < 4; ++i)
                for (var j:int = 0; j < 4; ++j)
                    for (var k:int = 0; k < 4; ++k)
                        localMatrix.m[i][j] += this.m[i][k] * paramMatrix.m[k][j];
 
            return localMatrix;
        }
 
        public function apply(paramVect:Vect):Vect {
            var localVect:Vect = new Vect(0.0, 0.0, 0.0);
            for (var i:int = 0; i < 4; ++i)
                for (var j:int = 0; j < 4; ++j)
                    localVect.d[i] += paramVect.d[j] * m[j][i];
 
            return localVect;
        }
 
        public function copy():Matrix {
            var localMatrix:Matrix = new Matrix();
            for (var i:int = 0; i < 4; ++i)
                for (var j:int = 0; j < 4; ++j)
                    localMatrix.m[i][j] = this.m[i][j];
 
 
            return localMatrix;
        }    
    }
}

And the main Metaball class :

C#
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
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
package {
    
    /**
     * @author Gordon Williams (http://www.rabidhamster.org/index.php)
     *
     * Java to Actionscript port by:
     * @author Devon O. Wolfgang (https://blog.onebyonedesign.com)
     */
 
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.utils.getTimer;
 
 
    public class MetaBall extends Sprite {
        
        private var MIDX:int;
        private var MIDY:int;
        private var MAXX:int;
        private var MAXY:int;
        
        private var _oldtime:Number;
        private var _newtime:Number;
        private var _timepass:Number;
        private var _time:Number;
        private var _pMatrix:Matrix;
        private var _pixels:Vector.<uint>;
        private var _grid:Vector.<Number> = new Vector.<Number>();
        private var _thresh:Number;
        private var _balls:Vector.<Vect> = new Vector.<Vect>();
        private var _ballFreq:Vector.<Vect> = new Vector.<Vect>();
        private var _ballOfs:Vector.<Vect> = new Vector.<Vect>();
        
        private var _meshData:Array;
        private var _squareData:Array;
        
        private var _canvas:BitmapData;
        private var _display:Bitmap;
        
        public function MetaBall(width:int, height:int, numBalls:int = 5) {
            
            _canvas = new BitmapData(width, height, false, 0x000000);
            _display = new Bitmap(_canvas);
            addChild(_display);
            
            _oldtime = 0;
            _newtime = 0;
            _timepass = 0.0;
            _time = 0.0;
 
            _grid.length = 4096;
            _grid.fixed = true;
            
            _thresh = 0.23;
            
            _balls.length = numBalls;
            _balls.fixed = true;
            _ballFreq.length = numBalls;
            _ballFreq.fixed = true;
            _ballOfs.length = numBalls;
            _ballOfs.fixed = true;
            
            MIDX = int(_canvas.width * .5);
            MIDY = int(_canvas.height * .5);
            MAXX = _canvas.width;
            MAXY = _canvas.height;
 
            _meshData = [ [], [ 0, 3 ], [ 1, 0 ], [ 1, 3 ], [ 3, 2 ], [ 0, 2 ],[ 3, 0, 1, 2 ],[ 1, 2 ], [ 2, 1 ], [ 0, 1, 2, 3 ], [ 2, 0 ], [ 2, 3 ], [ 3, 1 ], [ 0, 1], [ 3, 0 ], [] ];
 
            _squareData = [];
            
            init();
        }
 
        public function init():void {
            initBuffers();
            
            var len:int = _balls.length;
            for (var i:int = 0; i < len;  ++i) {
                _ballFreq[i] = new Vect(Math.random() + 0.5, Math.random() + 0.5, Math.random() + 0.5);
                _ballOfs[i] = new Vect(Math.random() * Math.PI, Math.random() * Math.PI, Math.random() * Math.PI);
            }
        }
 
        protected function initBuffers():void {
            _pixels = new Vector.<uint>();
            _pixels.length = _canvas.width * _canvas.height;
            _pixels.fixed = true;
        }
 
        private function ballVal(paramFloat1:Number, paramFloat2:Number, paramFloat3:Number):Number {
            var f:Number = paramFloat1 * paramFloat1 + paramFloat2 * paramFloat2 + paramFloat3 * paramFloat3;
            if (f > 1.0) return (1.0 / f);
            return 1.0;
        }
 
        private function gridFill():void {
            var i:int = 0;
            var len:int = _balls.length;
            for (var j:int = 0; j < 16; ++j)
                for (var k:int = 0; k < 16; ++k)
                    for (var l:int = 0; l < 16; ++l) {
                        _grid[i] = 0.0;
                        for (var i1:int = 0; i1 < len; ++i1)
                            _grid[i] += ballVal(l + _balls[i1].d[0] - 8.0, k + _balls[i1].d[1] - 8.0, j + _balls[i1].d[2] - 8.0);
                        ++i;
                    }
        }
 
        private function diff(paramFloat1:Number, paramFloat2:Number):Number {
            if (paramFloat2 != paramFloat1) {
                var f:Number = (_thresh - paramFloat1) / (paramFloat2 - paramFloat1);
 
                return f;
            }
            return 0.0;
        }
 
        private function getVectXY(paramInt:int, paramFloat1:Number, paramFloat2:Number, paramFloat3:Number):Vect {
            switch (paramInt) {
                case 0:
                    return new Vect(paramFloat1 + diff(_squareData[0], _squareData[1]), paramFloat2, paramFloat3);
                case 1:
                    return new Vect(paramFloat1 + 1.0, paramFloat2 + diff(_squareData[1], _squareData[3]), paramFloat3);
                case 2:
                    return new Vect(paramFloat1 + diff(_squareData[2], _squareData[3]), paramFloat2 + 1.0, paramFloat3);
                case 3:
                    return new Vect(paramFloat1, paramFloat2 + diff(_squareData[0], _squareData[2]), paramFloat3);
                default:
                    return new Vect(0.0, 0.0, 0.0);
            }
        }
 
        private function getVectYZ(paramInt:int, paramFloat1:Number, paramFloat2:Number, paramFloat3:Number):Vect {
            switch (paramInt) {
                case 0:
                    return new Vect(paramFloat1, paramFloat2 + diff(_squareData[0], _squareData[1]), paramFloat3);
                case 1:
                    return new Vect(paramFloat1, paramFloat2 + 1.0, paramFloat3 + diff(_squareData[1], _squareData[3]));
                case 2:
                    return new Vect(paramFloat1, paramFloat2 + diff(_squareData[2], _squareData[3]), paramFloat3 + 1.0);
                case 3:
                    return new Vect(paramFloat1, paramFloat2, paramFloat3 + diff(_squareData[0], _squareData[2]));
                default:
                    return new Vect(0.0, 0.0, 0.0);
            }
        }
 
        private function getVectZX(paramInt:int, paramFloat1:Number, paramFloat2:Number, paramFloat3:Number):Vect {
            switch (paramInt) {
                case 0:
                    return new Vect(paramFloat1, paramFloat2, paramFloat3 + diff(_squareData[0], _squareData[1]));
                case 1:
                    return new Vect(paramFloat1 + diff(_squareData[1], _squareData[3]), paramFloat2, paramFloat3 + 1.0);
                case 2:
                    return new Vect(paramFloat1 + 1.0, paramFloat2, paramFloat3 + diff(_squareData[2], _squareData[3]));
                case 3:
                    return new Vect(paramFloat1 + diff(_squareData[0], _squareData[2]), paramFloat2, paramFloat3);
                default:
                    return new Vect(0.0, 0.0, 0.0);
            }
        }
 
        private function gridMesh():void {
            var i:int = 0;
            
            for (var j:int = 0; j < 15; ++j)
                for (var k:int = 0; k < 16; ++k)
                    for (var l:int = 0; l < 16; ++l) {
                        if ((l < 15) && (k < 15)) {
                            var i1:int = 0;
                            if ((_squareData[0] = _grid[i]) > _thresh) ++i1;
                            if ((_squareData[1] = _grid[(i + 1)]) > _thresh) i1 += 2;
                            if ((_squareData[2] = _grid[(i + 16)]) > _thresh) i1 += 4;
                            if ((_squareData[3] = _grid[(i + 17)]) > _thresh) i1 += 8;
                            var len:int = _meshData[i1].length;
                            for (var i2:int = 0; i2 < len; i2 += 2)
                                line(getVectXY(_meshData[i1][i2], l, k, j), getVectXY(_meshData[i1][(i2 + 1)], l, k, j));
 
                            i1 = 0;
                            if ((_squareData[0] = _grid[i]) > _thresh) ++i1;
                            if ((_squareData[1] = _grid[(i + 16)]) > _thresh) i1 += 2;
                            if ((_squareData[2] = _grid[(i + 256)]) > _thresh) i1 += 4;
                            if ((_squareData[3] = _grid[(i + 272)]) > _thresh) i1 += 8;
                            len = _meshData[i1].length;
                            for (var i3:int = 0; i3 < len; i3 += 2)
                                line(getVectYZ(_meshData[i1][i3], l, k, j), getVectYZ(_meshData[i1][(i3 + 1)], l, k, j));
 
                            i1 = 0;
                            if ((_squareData[0] = _grid[i]) > _thresh) ++i1;
                            if ((_squareData[1] = _grid[(i + 256)]) > _thresh) i1 += 2;
                            if ((_squareData[2] = _grid[(i + 1)]) > _thresh) i1 += 4;
                            if ((_squareData[3] = _grid[(i + 257)]) > _thresh) i1 += 8;
                            len = _meshData[i1].length;
                            for (var i4:int = 0; i4 < len; i4 += 2)
                                line(getVectZX(_meshData[i1][i4], l, k, j), getVectZX(_meshData[i1][(i4 + 1)], l, k, j));
                        }
                        ++i;
                    }
        }
 
        private function line(paramVect1:Vect, paramVect2:Vect):void {
            paramVect1 = _pMatrix.apply(paramVect1);
            paramVect2 = _pMatrix.apply(paramVect2);
            paramVect1.project();
            paramVect2.project();
            drawLineAA(paramVect1.d[0] + MIDX, paramVect1.d[1] + MIDY, paramVect2.d[0] + MIDX, paramVect2.d[1] + MIDY, 100);
        }
 
        public function paint():void {
            
            var i:int = _canvas.width * _canvas.height;
            var j:int = _canvas.width;
            
            
            for (var k:int = j; k < i - j; ++k) {
                // original line
                //this._pixels[k] = ((this._pixels[k] & 0xFF) * 7 + ((this._pixels[(k - 1)] & 0xFF) + (this._pixels[(k + 1)] & 0xFF) + (this._pixels[(k - j)] & 0xFF) + (this._pixels[(k + j)] & 0xFF)) * 2 >> 4);
                
                this._pixels[k] = ((_pixels[k] & 0xFF) * 3 + ((_pixels[(k - 1)] & 0xFF) + (_pixels[(k + 1)] & 0xFF) + (_pixels[(k - j)] & 0xFF) + (_pixels[(k + j)] & 0xFF)) * 2 >> 4);
                
            }
 
            _oldtime = _newtime;
            _newtime = getTimer();
            _timepass = _newtime - _oldtime;
            _timepass /= 1000.0;
            _time += _timepass;
 
            var localMatrix1:Matrix = new Matrix();
 
            var localMatrix2:Matrix = new Matrix();
            localMatrix2.setTranslate(-8.0, -8.0, -8.0);
            localMatrix1.setRotateX(_time);
            localMatrix2 = localMatrix2.mul(localMatrix1);
            localMatrix1.setRotateY(_time * 1.375);
            localMatrix2 = localMatrix2.mul(localMatrix1);
            localMatrix1.setScale(5.0, 5.0, 5.0);
            _pMatrix = localMatrix2.mul(localMatrix1);
 
            var l:int = _balls.length;
            while (l--)  {
                _balls[l] = new Vect((Math.sin(_ballFreq[l].d[0] * _time + _ballOfs[l].d[0]) * 3.5), (Math.sin(_ballFreq[l].d[1] * _time + _ballOfs[l].d[1]) * 3.5), (Math.sin(_ballFreq[l].d[2] * _time + _ballOfs[l].d[2]) * 3.5));
            }
 
            gridFill();
 
            gridMesh();
            
            _canvas.lock();
            var kk:int = 0;
            var ii:int = _canvas.height;
            while (ii--) {
                var jj:int = _canvas.width;
                while(jj--) {
                    _canvas.setPixel(jj, ii, _pixels[kk]);
                    kk++;
                }
            }
            _canvas.unlock();
        }
 
        public function start():void {
            _newtime = (_oldtime = getTimer());
            addEventListener(Event.ENTER_FRAME, update);
        }
 
        public function stop():void {
            removeEventListener(Event.ENTER_FRAME, update);
        }
 
        private function update(event:Event):void {
            paint();
        }
 
        private function drawLineAA(paramDouble1:Number, paramDouble2:Number, paramDouble3:Number, paramDouble4:Number,  paramInt:int):void {
            var i7:int;
            var i8:int;
            var i9:int;
            var i10:int;
            var i:int = int(65536.0 * paramDouble1);
            var j:int = int(65536.0 * paramDouble3);
            var k:int = int(65536.0 * paramDouble2);
            var l:int = int(65536.0 * paramDouble4);
            var i1:int = i;
            var i2:int = j;
            var i3:int = k;
            var i4:int = l;
            var i5:int = i2 - i1;
            var i6:int = i4 - i3;
 
            if (Math.abs(paramDouble3 - paramDouble1) > Math.abs(paramDouble4 - paramDouble2)) {
                if (i2 < i1) {
                    i1 = j;
                    i2 = i;
                    i3 = l;
                }
 
                i7 = i3;
                i8 = (i5 >> 16 == 0) ? i6 : i6 / (i5 >> 16);
 
                for (i9 = i1 >> 16; i9 < i2 + 32767 >> 16; ++i9) {
                    i10 = i7 >> 16;
                    drawPixelAA(i9, i10, _canvas.width, i7, paramInt);
                    i7 += i8;
                }
            } else {
                if (i4 < i3) {
                    i1 = j;
                    i3 = l;
                    i4 = k;
                }
 
                i7 = i1;
                i8 = (i6 >> 16 == 0) ? i5 : i5 / (i6 >> 16);
 
                for (i9 = i3 >> 16; i9 < i4 + 32767 >> 16; ++i9) {
                    i10 = i7 >> 16;
                    drawPixelAA(i10, i9, 1, i7, paramInt);
                    i7 += i8;
                }
            }
        }
 
        private function drawPixelAA(paramInt1:int, paramInt2:int, paramInt3:int, paramInt4:int, paramInt5:int):void {
            if ((paramInt2 >= 0) && (paramInt2 < _canvas.height - 1) && (paramInt1 >= 0) && (paramInt1 < _canvas.width - 1)) {
                var i:int = paramInt2 * _canvas.width + paramInt1;
                if ((i < 0) || (i >= _canvas.width * _canvas.height)) return;
 
                var l1:Number = this._pixels[i];
                var l2:Number = this._pixels[(i + paramInt3)];
 
                if (l1 < 0) l1 = 256 + l1;
                if (l2 < 0) l2 = 256 + l2;
                
                var l3:Number = paramInt4 & 0xFFFF;
                var l4:Number = 65536 - l3;
 
                var l5:Number = paramInt5 * l4 >> 16;
                var l6:Number = paramInt5 * l3 >> 16;
 
                l1 += l5;
                l2 += l6;
 
                this._pixels[i] = ((l1 < 255) ? l1 : 0x000000);
                if (paramInt3 != 0) {
                    i += paramInt3;
                    if ((i < 0) || (i >= _canvas.width * _canvas.height)) return;
 
                    this._pixels[i] = ((l2 < 255) ? l2 : 0x000000);
                }
            }
        }
    }
}

Intantiation is easy – just create a new Metaball instance specifying its width, height and the number of balls (defaults to five) and add it to your display list.

Have no idea what you could do with the thing, but it sure is purty…

  Facebook   Pinterest   Twitter   Google+
metaball
  • Kitschy Cool
    December 29, 2007 · 0 comments
    1498
    3
    Read more
  • Fred Never Had it so Fun – 1st Crack at Unity
    January 09, 2010 · 14 comments
    3210
    6
    Read more
  • Real Drawing to Augmented Reality
    October 23, 2009 · 4 comments
    7045
    4
    Read more
3 Comments:
  1. If you like metaballs, you might like this guys stuff. He demo’d at Flash On rThe Beach last week.

    http://prinzipiell.com/

    It’s not russian bride spam honest.

    Glen · October 02, 2009
  2. I did see that guy speak at Elevator Pitch session. Great stuff. Thanks for the link Glen. And for not posting russion bride spam…

    Devon O. · October 06, 2009
  3. this is pretty cool. Nice work

    dominic · November 01, 2009

Leave a Comment! Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Devon O. Wolfgang

AIR | Unity3D | AR/VR

Unity Certified Developer

Technical Reviewer of “The Essential Guide to Flash CS4 AIR Development” and “Starling Game Development Essentials”

Reviewer of “The Starling Handbook”

Unity Engineer at Touch Press.

Categories
  • Actionscript (95)
  • AIR (16)
  • Flash (99)
  • Games (7)
  • Liberty (13)
  • Life (53)
  • Shaders (20)
  • Unity3D (21)
Recent Comments
  • MainDepth on Unity Ripple or Shock Wave Effect
  • Devon O. on Unity Ripple or Shock Wave Effect
  • Feral_Pug on Unity Ripple or Shock Wave Effect
  • bavvireal on Unity3D Endless Runner Part I – Curved Worlds
  • Danielius Vargonas on Custom Post Processing with the LWRP
Archives
  • December 2020 (1)
  • December 2019 (1)
  • September 2019 (1)
  • February 2019 (2)
  • December 2018 (1)
  • July 2018 (1)
  • June 2018 (1)
  • May 2018 (2)
  • January 2018 (1)
  • December 2017 (2)
  • October 2017 (1)
  • September 2017 (2)
  • January 2017 (1)
  • July 2016 (1)
  • December 2015 (2)
  • March 2015 (1)
  • September 2014 (1)
  • January 2014 (1)
  • August 2013 (1)
  • July 2013 (1)
  • May 2013 (1)
  • March 2013 (2)
  • December 2012 (1)
  • November 2012 (1)
  • September 2012 (3)
  • June 2012 (2)
  • May 2012 (1)
  • April 2012 (1)
  • December 2011 (2)
  • October 2011 (3)
  • September 2011 (1)
  • August 2011 (1)
  • July 2011 (1)
  • May 2011 (2)
  • April 2011 (2)
  • March 2011 (1)
  • February 2011 (1)
  • January 2011 (2)
  • December 2010 (3)
  • October 2010 (5)
  • September 2010 (1)
  • July 2010 (2)
  • May 2010 (5)
  • April 2010 (2)
  • March 2010 (7)
  • February 2010 (5)
  • January 2010 (5)
  • December 2009 (3)
  • November 2009 (1)
  • October 2009 (5)
  • September 2009 (5)
  • August 2009 (1)
  • July 2009 (1)
  • June 2009 (2)
  • May 2009 (6)
  • April 2009 (4)
  • March 2009 (2)
  • February 2009 (4)
  • January 2009 (1)
  • December 2008 (5)
  • November 2008 (2)
  • September 2008 (1)
  • August 2008 (6)
  • July 2008 (6)
  • June 2008 (9)
  • May 2008 (4)
  • April 2008 (3)
  • March 2008 (4)
  • February 2008 (9)
  • January 2008 (7)
  • December 2007 (6)
Copyright © 2021 Devon O. Wolfgang