bash generate random words

Recently, I needed some random words for test data so I used bash to generate them:

#!/usr/local/bin/bash
# requires bash 4, see http://clubmate.fi/upgrade-to-bash-4-in-mac-os-x/

# load up the dictionary
mapfile dict < /usr/share/dict/words

# count lines in dicitonary
WC=$(cat -n /usr/share/dict/words | wc -l)

# default to 50 words unless user passes param
NUM_WORDS=50
if [ $# -eq 1 ]; then
if echo "$1" | grep -qE ^\-?[0-9]+$; then
NUM_WORDS="$1"
fi
fi

# create file for output
rm -f random_words.txt && touch random_words.txt

# pick random words
for i in `seq 1 $NUM_WORDS`; do
# generate a single number within WC range using jot
RAND_INDEX=$(jot -r 1 1 $WC)
echo ${dict[$RAND_INDEX]} >> random_words.txt
done

how to make a unicorn

Managment: We need a unicorn, how long will it take to make one?

Artist: I’ve never drawn a unicorn before, I…

Management: Nevermind that, I need a number. Give me a breakdown of how long it will take to draw each body part.

Artist: Uhh…okay. 2 days for the head, 1 day for the body, 0.25 days for each of the limbs, I guess.

Management: Okay! I have you scheduled to draw four limbs before end of day. Tomorrow you can draw the head and finish off the week attaching the body to both.

Later that week…

unicorn

Management: Shit, we forgot the tail. We should fix the alignment issues between the head and body but we’ve run out of time. Meh. Ship it.

wavefront OBJ file format parsing with bash

Recently, I needed to extract some vertices from an OBJ file and drop them into my code. Rather than writing a OBJ file parser, I used bash to process the text. Here’s the OBJ file for a simple cube exported from Blender:

# Blender v2.71 (sub 0) OBJ File: ''
# www.blender.org
v -1.000000 -1.000000 1.000000
v -1.000000 -1.000000 -1.000000
v 1.000000 -1.000000 -1.000000
v 1.000000 -1.000000 1.000000
v -1.000000 1.000000 1.000000
v -1.000000 1.000000 -1.000000
v 1.000000 1.000000 -1.000000
v 1.000000 1.000000 1.000000
s off
f 6 2 1
f 7 3 2
f 8 4 3
f 5 1 4
f 2 3 4
f 7 6 5
f 5 6 1
f 6 7 2
f 7 8 3
f 8 5 4
f 1 2 4
f 8 7 5

In bash, I cd to the relevant file and run:

$ cat cube.obj | grep "^v " | cut -c 3- | xargs printf '%.1f,' | xargs printf 'static const float positions[] = { %s };' | pbcopy

Then I go to my source code and Cmd+V to paste:

static const float positions[] = { -1.0,-1.0,1.0,-1.0,-1.0,-1.0,1.0,-1.0,-1.0,1.0,-1.0,1.0,-1.0,1.0,1.0,-1.0,1.0,-1.0,1.0,1.0,-1.0,1.0,1.0,1.0, };

Nifty. I’ll likely extend it to extract additional data, compile to a custom binary format, and save it out to a shell script. After that I can call my make binary obj from either the command line or Xcode:

$ mbo cube.obj

Update:

Full script to create a binary OBJ file with an interleaved vertex buffer (v/n/uv).

#!/bin/bash
obj=$(<$1)
vertices=($(echo "$obj" | grep "^v " | cut -c 3- | xargs printf '%f '))
normals=($(echo "$obj" | grep "^vn " | cut -c 4- | xargs printf '%f '))
uvs=($(echo "$obj" | grep "^vt " | cut -c 3- | xargs printf '%f '))
faces=($(echo "$obj" | grep "^f" | cut -c 3- | tr '/' ' ' | xargs -n 1 expr -1 +))
buffer=()
for (( i = 0 ; i < ${#faces[@]}; i+=3 )) do
vi=$((${faces[$(($i+0))]}*3))
ui=$((${faces[$(($i+1))]}*2))
ni=$((${faces[$(($i+2))]}*3))
buffer+=("${vertices[$(($vi+0))]}")
buffer+=("${vertices[$(($vi+1))]}")
buffer+=("${vertices[$(($vi+2))]}")
buffer+=("${normals[$(($ni+0))]}")
buffer+=("${normals[$(($ni+1))]}")
buffer+=("${normals[$(($ni+2))]}")
buffer+=("${uvs[$(($ui+0))]}")
buffer+=("${uvs[$(($ui+1))]}")
done
vertexFormat=$(printf "%s," "${buffer[@]}")
vertexFormat=${vertexFormat%?}
main="#import <Foundation/Foundation.h>
static float buffer[] = { $vertexFormat };
int main(int argc, const char* argv[]) {
NSData* data = [NSData dataWithBytes:&buffer length:sizeof(buffer)];
[data writeToFile:@\"$2.mbo\" atomically:YES];
return 0;
}"
echo "${main}" > main.m
clang -fobjc-arc main.m -o main_app
./main_app
rm main.m main_app
echo now add the file to xcode

To use the script:

  1. Copy the text into a file called mbo.sh
  2. chmod +x  mbo.sh
  3. ./mbo.sh cube.obj cube

Then load it into a vertex buffer. Using Metal in this example:

NSURL* modelUrl = [[NSBundle mainBundle] URLForResource:@”cube” withExtension:@”mbo”];

        NSData* modelBinData = [NSData dataWithContentsOfURL:modelUrl];

        _vertexBuffer = [_device newBufferWithBytes:modelBinData.bytes length:modelBinData.length options:MTLResourceOptionCPUCacheModeDefault];