Thursday, March 21, 2013

WebGL: Fixing "INVALID_OPERATION: drawArrays: attribs not setup correctly"

I kept getting the WebGL: INVALID_OPERATION: drawArrays: attribs not setup correctly error in my simple WebGL app, which wasn't very helpful to me. Ok, I get it's not setup correctly, but what could be wrong? It turns out the problem was being caused by me using two different shader programs, with each program having a different number of attributes. I want to walk through this clearly, because most of the things I found online when searching for solutions were... lacking, to put it kindly. Let's look at my shaders:

Shader Program #1

It's a pretty simple program. Given a set of 2D vertices, make them all white.

Vertex shader:
attribute vec2 vertex;

void main(void)
{
    gl_Position = vec4(vertex, 0.0, 1.0);
}
Fragment shader:
precision mediump float;

void main(void)
{
    gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}

Shader Program #2

Not too complicated. Given a set of 2D vertices and corresponding texture coordinates, render a textured object.

Vertex shader:
attribute vec2 vertex;
attribute vec2 textureCoord;

varying vec2 fragTextureCoord;

void main(void)
{
    gl_Position = vec4(vertex, 0.0, 1.0);
    fragTextureCoord = textureCoord;
}
Fragment shader:
precision mediump float;

uniform sampler2D sampler;

varying vec2 fragTextureCoord;

void main(void)
{
    gl_FragColor = texture2D(sampler, fragTextureCoord);
}

The problem

The problem is rooted in the shaders. The first vertex shader only has one attribute (vertex), while the second vertex shader has two attributes (vertex and textureCoord). When switching from one program to another, the enabled attributes weren't getting switched! In other words, when I loaded shader program #1 and called enableVertexAttribArray to enable the vertex attribute, and then later when I loaded shader program #2 and called enableVertexAttribArray to enable the vertex and textureCoord attributes, the enabling of the attributes isn't bound to a specific program. Enabling an attribute is a global operation, regardless of whether or not you create or use a different program. So when using shader program #1, the textureCoord attribute was still enabled! This resulted in an error (which seems sensible). There are two ways to work around this.

Solution #1: disableVertexAttribArray

One option is to just disable the extra attributes. When we use shader program #1, we can call disableVertexAttribArray to disable the extra attributes that shader program #2 uses. You have to watch out a little if you do this, because if you aren't explicitly assigning index numbers to your attributes, it's possible the vertex attribute in shader programs #1 and #2 will have different index values. One way to do this is to track which program you were using and which program you're going to use now, and then disable any extra attributes (or enable any missing attributes). Something like:

function switchPrograms(currentProgram, newProgram)
{
    // Gets the number of attributes in the current and new programs
    var currentAttributes = gl.getProgramParameter(currentProgram, gl.ACTIVE_ATTRIBUTES);
    var newAttributes = gl.getProgramParameter(newProgram, gl.ACTIVE_ATTRIBUTES);

    // Fortunately, in OpenGL, attribute index values are always assigned in the
    // range [0, ..., NUMBER_OF_VERTEX_ATTRIBUTES - 1], so we can use that to
    // enable or disable attributes
    if (newAttributes > currentAttributes) // We need to enable the missing attributes
    {
        for (var i = currentAttributes; i < newAttributes; i++)
        {
            gl.enableVertexAttribArray(i);
        }
    }
    else if (newAttributes < currentAttributes) // We need to disable the extra attributes
    {
        for (var i = newAttributes; i < currentAttributes; i++)
        {
            gl.disableVertexAttribArray(i);
        }
    }

    // With all the attributes now enabled/disabled as they need to be, let's switch!
    gl.useProgram(newProgram);
}

Note that the above solution assumes all your attributes are array attributes. If you have an attribute that isn't an array (i.e. it shouldn't be enabled with enableVertexAttribArray), you'll have to come up with a more robust solution. Hopefully the above at least gives you some ideas and can help point you in the right direction in your quest to come up with a valid solution.

Solution #2: vertexAttribPointer

Enable all the attributes! Alright, so the whole reason we were getting this INVALID_OPERATION error comes from here (I suggest you read it and try to understand it!). The key here is in the last statement: "If a vertex attribute is enabled as an array, a buffer is bound to that attribute, but the attribute is not consumed by the current program, then regardless of the size of the bound buffer, it will not cause any error to be generated during a call to drawArrays or drawElements."

This means that if your program doesn't use the attribute, it's okay to fill it with a tiny junk array (note: not null, as that has other implications), as it won't actually be used. This means you can bind a tiny junk buffer with bindBuffer and then set the superfluous attribute to that tiny junk buffer with vertexAttribPointer, and then continue on your merry way. All WebGL wants is for your enabled array attributes to be associated with some valid (non-null) array (which is why you get the INVALID_OPERATION error if they're not). If you enable all the attributes, but your program doesn't actually use an enabled attribute, WebGL doesn't care how big the array is that's associated with it (so you won't get any out-of-bounds errors). It's just happy it has an array associated with the enabled attribute.

Saturday, March 2, 2013

ASCII Snake

I've always thought the classical game of Snake is a fun game to program. One day I decided I wanted to speed-code a game of Snake, rendered entirely with (extended) ASCII art. One particular friend of mine made feature requests and gave me feedback as I quickly developed it.

In the end, the game was quite a bit of fun. It ran in a Windows console, had color, cheats, Easter eggs, and even a multiplayer mode (that operated kind of like a Tron Lightcycle battle). My friend and I made several good memories with this little game, competing for the high score and battling it out in multiplayer. If everything was quiet and a sudden "Dang it!" came echoing from another room, I knew a game of Snake was going down.

Because I was speed coding this, I'm not going to release the code (it's atrocious!). I will, however, for kicks and giggles, provide binaries which you may play with. You can download it here. See if you can figure out all the cheats and Easter eggs!

Haskell Mandelbrot

I wrote a program (years ago) in Haskell that generated the image above of the Mandelbrot set. I really like Haskell, but I haven't spent enough time with it and never really wrapped my head around monads. I'd like to dive back into it when I get the time.

Anyway, the program I used to generate the fractal is below. I'm not a Haskell guru, so if the code is an abomination and you know how to improve it, please share in the comments! It would be cool to improve my Haskell skills. I wrote this years ago:

module Main where

import Data.Complex
import Data.Char
import qualified Data.ByteString.Char8 as C -- For file output

-- Constants
maxIter :: Int -- Max iterations
maxIter = 750

width  :: Int -- Image width
height :: Int -- Image height

width  = 400
height = 400

-- Note: aspect ratio of (minX, minY), (maxX, maxY) must
-- match aspect ratio of (width, height)
minX :: Double -- Min x-coordinate of graph
maxX :: Double -- Max x-coordinate of graph
minY :: Double -- Min y-coordinate of graph
maxY :: Double -- Max y-coordinate of graph

-- For the zoomed in part of the Mandelbrot:
--minX = -0.826341244461360116
--maxX = -0.8026423086165848822

--minY = -0.2167936114403439588
--maxY = -0.193094675595568725

--For a full view of the mandelbrot
minX = -2.5
maxX = 1.5

minY = -2
maxY = 2


-- The actual fractal part
-- It basically works on a matrix, which we will call M, that represents a grid of
-- points on the graph. Essentially, M[i, j] is (xList[j], yList[i])
xList :: [Double]
yList :: [Double]

xList = [minX, (minX + ((maxX - minX) / (fromIntegral width  - 1)))..maxX]
yList = reverse [minY,(minY + ((maxY - minY) / (fromIntegral height - 1)))..maxY]

row :: Double -> C.ByteString -- A row of image bytes for a given y-coordinate
row y = C.pack [frac (x :+ y) (0 :+ 0) 0 | x <- xList] -- For Mandelbrot set
--row y = C.pack [frac ((-0.1) :+ (0.8)) (x :+ y) 0 | x <- xList] -- For Julia set

etaFraction :: Complex Double -> Double
etaFraction z = (log (log (magnitude z))) / log 2

smoothEta :: Int -> Complex Double -> Double -- Smooth escape time algorithm value
smoothEta iter z = (fromIntegral iter - etaFraction z) / fromIntegral maxIter

color :: Int -> Complex Double -> Double -- Gets the color for the point, in range [0, 1]
color iter z = 1 - smoothEta iter z -- Smooth escape time algorithm (and invert)
--color iter z = fromIntegral iter / fromIntegral maxIter

interpolate :: Double -> Char -- Adds an interpolation curve for interpolating color
interpolate v = chr (truncate ((v ^ 12) * 255)) -- Polynomial curve
--interpolate v = chr (truncate(v * 255)) -- Linear

frac :: Complex Double -> Complex Double -> Int -> Char -- The actual fractal algorithm!
frac c z iter
     | iter >= maxIter = chr 255 -- never escaped, return color value of 255
     | otherwise = let z' = z * z + c
               in if ((realPart z') * (realPart z') + (imagPart z') * (imagPart z')) > 4
                  then interpolate (color iter z')
    else frac c z' (iter + 1)


-- The file output
pgmHeader :: C.ByteString
pgmHeader = C.pack ("P5\n" ++ (show width) ++ " " ++ (show height) ++ "\n255\n")
main = C.writeFile "fractal.pgm" (C.append pgmHeader (C.concat [(row y) | y <- yList]))

And some pictures from this program (each image has had some constants tweaked):


The Mandelbrot set in pure black and white; only pixels considered "inside" the set are black.


A Julia set based on (0.285, -0.01i). I actually edited the brightness/contrast in post-processing on this one to really bring out the spirals.


A zoomed in portion of the Mandelbrot set. I edited this one's brightness/contrast settings in post processing to bring out some of the little details.

Sunday, February 24, 2013

Tweener Demo

This is the second Android app I ever made, and I'm quite happy with it. Its focus is a custom view/control I made, named TweenerControl. As the name (hopefully) implies, it allows you to create tweening/easing curves that can later be used for a variety of things, including view animations. The TweenerControl creates a Tweener object, which implements the android.view.animation.Interpolator interface.

The motivation for the project was to be able to visually and interactively create tweening curves that could be used for a wide range of things. There are various interpolators offered by Android, but it's hard to visualize exactly what an AnticipateInterpolator really looks like, for example.

My tweener works by creating a series of cubic curves and connecting them into a single spline. At first I looked into using cubic Hermite splines, Bézier curves, and Kochanek-Bartels curves, but all of these were problematic because they provide two output values from one input value, and tweening functions need to produce one output value from one input value. Overall, I'm happy with the results the cubic curves give.

For the curious, the cubic curves were generated as follows: a cubic function f(x) is generated from two end points and the slope at the end points. That is, given two points, (x1, y1) and (x2, y2) that lie on the function f(x), and the slope at these points (m1 = f'(x1) and m2 = f'(x2)), the cubic function f(x) = ax3 + bx2 + cx + d is constructed satisfying these constraints. It's just a matter of a little linear algebra. A series of cubic curves are then pieced together into a single spline, representing the overall function that maps inputs in the range [0, 1] to a "tweened" output.

I'll work on getting the code repository online somewhere. Note that I will not be putting this on the Google Play Store, simply because it's a little demo and not something I think should clutter the Play Store.

In the Tweener Demo, there is a box along the top that moves back and forth along the top of the screen according to the curve. Different color schemes can be selected, and the color transitions according to the curve as well. There's a vertical line that moves across the curve as the box moves, showing you which part of the curve is causing the box to be at its specific location. Control points can be added by double-tapping the curve. A control point can be deleted by double-tapping the it. Here's some screenshots:


The initial screen.


The "Android" theme selected, with the curve moved around a bit.


The "Dark" theme selected, with the view zoomed in on a part of the curve.


The "Colorful" theme selected, with extra control points added.

Tuesday, February 5, 2013

Macs are PCs

I know. I know what people mean when they say "Mac" and "PC." But it's still wrong. "PC" is an abbreviation for "Personal Computer," which yes, really is as vague as it sounds. Your MacBook Pro is a personal computer, your iMac is a personal computer, your Dell Inspiron is a personal computer, your Chromebook is a personal computer, etc. They're all PCs. Please, for the love of all that is technically correct, stop pretending like Macs are something different and special from a PC.

If you're going to compare, say "Mac" and "Windows" or "Mac" and "Windows PC." "Mac" is a bit confusing, because it's the name of an operating system and also a product line of personal computers. If you're going to compare operating systems, say "Mac" and "Windows," seeing as they're both operating systems and now you're comparing software to software. Or better yet, say "OS X" and "Windows" because these days, the proper name is "OS X" and not "Mac OS X".

If you want to compare Macintosh (marketed as Mac) personal computers against Windows based personal computers, say "Mac" and (something like) "Windows PC." Without the "Windows," comparing Macs to PCs is like comparing apples to fruits... apples are fruits (think about it: if you were to say "Apples are way better than fruits," you'd sound like an idiot). Specifying "Windows" means now the comparison is specific and actually makes sense.

Saturday, February 2, 2013

Building SFML 2 with C++11 on OS X

It's not a very difficult thing to do, but clear, simple instructions can be useful. Thankfully, there's a good Getting Started guide with official documentation for building SFML (and you really should read it). If, after reading that, you want things spelled out even more... here you go.

Step 1: Prepare Xcode

Make sure you have Xcode downloaded and installed. Make sure you have your command line tools set up. You do this by doing Xcode -> Preferences -> Downloads and making sure you have Command Line Tools downloaded and installed.

Step 2: Prepare Cmake

I won't go into what Cmake is, but download and install the latest version. SFML needs it.

Step 3: Download SFML 2

Go to SFML's download page and download the SFML source code (either the SFML 2.0 source code or the latest snapshot of the Git repository).

Step 4: Open a Terminal

Hopefully you've got the Terminal sitting on your dock, but in case not it can be found under Applications -> Utilities -> Terminal.app.

Step 5: Set up Cmake

In your terminal, run:

export CMAKE_OSX_ARCHITECTURES='i386;x86_64'

This tells Cmake you're going to be making 32-bit and 64-bit universal builds. If you want only 32-bit, just set it to i386. If you want only 64-bit, set it to x86_64. I recommend building both though in a universal build. It'll help when you want to distribute your program.

Step 6: Get Ready to Build

Next, do the following in your terminal:

cd <sfml folder>
mkdir build
cd build

In the first line, replace <sfml folder> with the path to the folder where you downloaded and extracted the SFML source to. mkdir build just creates the directory build where we can do our dirty work, and cd build moves into that folder.

Step 7: Run Cmake

Now run the following (you can copy and paste):

cmake -G "Unix Makefiles" -DCMAKE_CXX_COMPILER=/usr/bin/clang++ \
      -DCMAKE_C_COMPILER=/usr/bin/clang -DSFML_BUILD_FRAMEWORKS=ON \
      -DSFML_INSTALL_XCODE4_TEMPLATES=ON \
      -DCMAKE_CXX_FLAGS="-stdlib=libc++" -DCMAKE_C_FLAGS="-stdlib=libc++" ../

This runs Cmake and tells it to do a few things. Particularly, it's saying "Use clang++ and clang to build (which is what gives us the C++11 features we're wanting), build frameworks, build templates for Xcode so I can easily create SFML projects, and make sure you use the C++11 standard library." This doesn't actually build SFML, but it sets everything up so we can run the next two commands...

Step 8: Build!

Now run:

make -j4

make is a fancy program that will actually do all the building of SFML for you. The -j4 option just says "Use 4 threads to do this." If you don't want to use multiple threads to build (which is sloooow), you can omit this option. If you want to use more threads, like 8, you can change it to -j8 (I personally use 8 threads).

Step 9: Install!

The last step:

sudo make install

This will prompt you for your password. Give it your password. It'll place all the SFML files in their final resting place on your system, and it needs some elevated privileges to write the files to the necessary folders. If you're curious where it's writing the files to, just look at the output. Anyway, now you're done!

Bonus Step: Making an Xcode Project

What good is building and installing SFML if you don't use it? We'll set up a simple project now. Open Xcode. Then create a new project. Then select the SFML App template:

In the next screen, give your project a name. Make sure to say you want to use C++11! The "C++ Compiler and Standard Library" option does not default to Clang and C++11. You have to set this yourself. Assuming you followed my instructions and made SFML a universal framework for 32-bit and 64-bit, the rest of the options can stay the same.

You should now be able to run the created project!

Monday, December 24, 2012

SWFer

Notice:

I have updated SWFer and have made several large changes. The code no longer sucks and it now supports more things. I don't have time to properly update this page currently (it needs a complete rewrite with my changes), so I will leave the original post below. The project is now hosted on Github. The latest version can be downloaded here.

The old original post/page:

SWFer is an application for decompiling and compiling SWF files. It is currently in a very early pre-alpha stage, but I will be making a pre-alpha release with very limited (but hopefully stable) functionality. It was inspired by the complete lack of gaplessly looping MP3 files in SWFs not made with Adobe Flash. I was working on a little game in FlashDevelop and was disappointed that I couldn't get the audio track to properly gaplessly loop. Given my prior experience with FFmpeg, I decided I would find out why and how Adobe Flash created properly looping MP3s and see if I couldn't make a tool to facilitate creating gapless MP3 loops.

The download for the current pre-alpha version of SWFer can be found here. Feedback can be submitted here. (Update: Please don't download this version, as it has been superseded by a new release; see the top of this page for more information)

I cannot stress the pre in pre-alpha release enough. The code is currently horrible, mostly because I've been hacking around at the binary level trying to get the generated SWFs to work as well as they do. I'm releasing this early because a) it's useful, and b) early feedback will help me improve SWFer. The code is going through a massive overhaul, however, even if the end-user doesn't see any noticeable changes.

How to use SWFer: A micro-tutorial

Simply run SWFer, select your source audio file (either a FLAC or WAV), adjust quality parameters (or leave them as they are), give the sound an ActionScript 3 class name, and save the SWF!

Most planned features are disabled in SWFer, and the only available feature is creating SWFs that contain a gaplessly looping MP3. I'll be the first to admit the user interface is not optimal, but for now I'm focusing on features instead of UI, and hopefully it's easy and obvious enough.

How do I use the generated SWF file in my FlashDevelop project?

Let's say you made a SWF file called "MySWF.swf" and inside it you put a sound that you gave the ActionScript 3 class name of "MySound". Loading it is as easy as:

package
{
    import flash.media.Sound;

    [Embed(source = 'MySWF.swf', symbol='MySound')]
    public class MyImportedSound extends Sound
    {
    }
}

Then, to use the imported sound, all you need to do is:

var s:Sound =  new MyImportedSound();
s.play(0, 0); // Feel free to loop using the last parameter of play()

Faux-FAQ

If I already have an MP3 file, can I use SWFer to make a gaplessly looping SWF?
Currently, no. SWFer needs a gapless audio file as a source input (currently I only allow WAV and FLAC) in order to generate a gapless SWF+MP3. MP3s always have an intro and outro gap (technically, it's possible to make a gapless MP3, but it's complex and no encoder+decoder that I know of does), so if you are using an already-existing MP3, SWFer can't do necessary pre-processing to determine how to skip the gaps. Someday, I'd like to add some automatic heuristic guessing to remove gaps from already existing MP3s and allow users to manually tweak settings.
What platforms are supported?
Currently, only Windows. The code is cross-platform, however, and I plan to release OS X and Linux builds (someday).
Does SWFer reduce the quality of my audio?
Not any more than encoding to an MP3 does. SWFer uses the FFmpeg project libraries and LAME, which is one of, if not the, highest quality MP3 encoders available.
How does SWFer gaplessly loop MP3s?
If you're tech-savvy and open a SWF file in a hex editor or some program, you can find two tags: a DefineSound tag, which actually defines a sound and contains its sample data, and the StartSound (or StartSound2) tag, which starts playing a sound and declares how many beginning samples to skip. If you know the number of samples for the starting and ending gaps in your MP3, you can set the StartSound tag to skip the right number of samples for the starting gap, and then set the right number of samples to play in the DefineSound tag so that it stops playing before the end gap starts.
What is SWFer's license?
SWFer is licensed under the GNU GPL. See its corresponding license file for more information. I may consider changing the license in the future, but I make no promises. Don't worry, your generated SWF files are yours to keep and don't fall under the GPL.
Where can I learn more about the SWF file format?
SWF File Format Specification Version 10
Where can I learn more about ActionScript 3 byte code?
ActionScript Virtual Machine 2 (AVM2) Overview
Why is it "Faux-FAQ" and not just "FAQ?"
Because I've never been asked any questions about this, so these aren't really frequently asked questions. They're just ones I assume are useful to know.