Skip to content

Instantly share code, notes, and snippets.

@MichaelSnowden
Created November 18, 2014 22:05
Show Gist options
  • Save MichaelSnowden/0d03155e3cbd20fcd04c to your computer and use it in GitHub Desktop.
Save MichaelSnowden/0d03155e3cbd20fcd04c to your computer and use it in GitHub Desktop.
//
// Cube.h
// OpenGL
//
// Created by Michael Snowden on 11/15/14.
// Copyright (c) 2014 MichaelSnowden. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <OpenGLES/ES1/glext.h>
// Axis indices are just a way of encoding x, y, and z as integers
// x = 0
// y = 1
// z = 2
// ...
// For example, a rotation in the xy plane (about the z axis then), uses a {0, 1} plane
typedef struct {
GLuint firstAxisIndex;
GLuint secondAxisIndex;
} Plane;
@interface Cube : NSObject
@property(assign, nonatomic) NSUInteger dim;
- (id)initWithDim:(NSUInteger) dim;
- (void)tearDownGL;
- (void)prepareToRender;
- (void)render;
- (void)rotateInPlane:(Plane) plane withAngle:(float) theta;
@end
//
// Cube.m
// OpenGL
//
// Created by Michael Snowden on 11/15/14.
// Copyright (c) 2014 MichaelSnowden. All rights reserved.
//
#import "Cube.h"
#import <OpenGLES/ES1/glext.h>
#import <GLKit/GLKit.h>
#include <vector>
#include <algorithm>
#define BUFFER_OFFSET(i) ((char *)NULL + (i))
#define IS_BIT_SET(var,pos) (var & 1<<pos)
#define MAGNITUDE 0.1f
std::vector<GLfloat> _vertexData;
std::vector<GLuint> _indexData;
@implementation Cube {
GLuint _vertexArray;
GLuint _vertexBuffer;
GLuint _indexBuffer;
}
- (id)initWithDim:(NSUInteger)dim {
if (self = [super init]) {
assert(dim > 0);
_dim = dim;
[self setupGeometry];
[self setupGL];
}
return self;
}
- (void)setupGL {
glGenVertexArraysOES(1, &_vertexArray);
glBindVertexArrayOES(_vertexArray);
glGenBuffers(1, &_vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, _vertexData.size() * sizeof(GLfloat), &_vertexData[0], GL_DYNAMIC_DRAW);
glGenBuffers(1, &_indexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, _indexData.size() * sizeof(GLuint), &_indexData[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, _dim, GL_FLOAT, GL_FALSE, _dim * sizeof(GLfloat), BUFFER_OFFSET(0));
}
- (void)setupGeometry {
_vertexData.resize(_dim * (1 << _dim)); // We have 2^dim vertices. i.e. 2^3 for a cube
[self setupVertices];
[self setupIndices];
}
- (void)setupVertices { // Creates vertices out of corresponding bit strings
for (int i = 0; i < _vertexData.size() / _dim; ++i) {
for (int j = 0; j < _dim; ++j) {
_vertexData[i*_dim+j] = IS_BIT_SET(i, j) ? MAGNITUDE : -MAGNITUDE;
}
}
}
- (void)setupIndices { // Connects all vertices that differ by 1 bit
_indexData.reserve(_vertexData.size());
for (int u = 0; u < (1 << _dim); ++u) {
for (int i = 1; i & ((1 << _dim) - 1); i <<= 1) {
if (!(u&i)) {
_indexData.push_back(u);
_indexData.push_back(u | i);
}
}
}
}
- (void)prepareToRender {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer);
glBindVertexArrayOES(_vertexArray);
}
- (void)render {
glDrawElements(GL_LINES, _indexData.size(), GL_UNSIGNED_INT, 0);
}
- (void)tearDownGL {
glDeleteBuffers(1, &_vertexBuffer);
glDeleteVertexArraysOES(1, &_vertexArray);
}
- (void)dealloc {
[self tearDownGL];
}
- (void)rotateInPlane:(Plane)plane withAngle:(float)theta {
assert(plane.firstAxisIndex < _dim);
assert(plane.secondAxisIndex < _dim);
for (int i = 0; i < _vertexData.size(); i += _dim) {
int i1 = i + plane.firstAxisIndex;
int i2 = i + plane.secondAxisIndex;
float temp = _vertexData[i1]*cos(theta) - _vertexData[i2]*sin(theta);
_vertexData[i2] = _vertexData[i1]*sin(theta) + _vertexData[i2]*cos(theta);
_vertexData[i1] = temp;
}
glBufferSubData(GL_ARRAY_BUFFER, 0, _vertexData.size() * sizeof(GLfloat), &_vertexData[0]);
}
@end
//
// ___VARIABLE_classPrefix:identifier___ViewController.m
// OpenGL
//
// Created by Michael Snowden on 11/15/14.
// Copyright (c) 2014 MichaelSnowden. All rights reserved.
//
#import "ViewController.h"
#import <OpenGLES/ES1/glext.h>
#import "Cube.h"
@interface ViewController () {
GLuint _program;
GLKMatrix4 _modelViewProjectionMatrix;
GLKMatrix3 _normalMatrix;
}
@property (strong, nonatomic) EAGLContext *context;
@property (strong, nonatomic) GLKBaseEffect *effect;
@property (strong, nonatomic) Cube *cube;
- (void)setupGL;
- (void)tearDownGL;
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
if (!self.context) {
NSLog(@"Failed to create ES context");
}
GLKView *view = (GLKView *)self.view;
view.context = self.context;
view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
[self setupGL];
}
- (void)dealloc
{
[self tearDownGL];
if ([EAGLContext currentContext] == self.context) {
[EAGLContext setCurrentContext:nil];
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
if ([self isViewLoaded] && ([[self view] window] == nil)) {
self.view = nil;
[self tearDownGL];
if ([EAGLContext currentContext] == self.context) {
[EAGLContext setCurrentContext:nil];
}
self.context = nil;
}
// Dispose of any resources that can be recreated.
}
- (void)setupGL
{
[EAGLContext setCurrentContext:self.context];
self.effect = [[GLKBaseEffect alloc] init];
self.effect.light0.enabled = GL_TRUE;
self.effect.light0.diffuseColor = GLKVector4Make(1.0f, 0.4f, 0.4f, 1.0f);
glEnable(GL_DEPTH_TEST);
_cube = [[Cube alloc] initWithDim:4];
float aspect = fabsf(self.view.bounds.size.width / self.view.bounds.size.height);
GLKMatrix4 projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(65.0f), aspect, 0.1f, 100.0f);
self.effect.transform.projectionMatrix = projectionMatrix;
GLKMatrix4 baseModelViewMatrix = GLKMatrix4MakeTranslation(0.0f, 0.0f, -4.0f);
baseModelViewMatrix = GLKMatrix4Rotate(baseModelViewMatrix, 0.f, 0.0f, 1.0f, 0.0f);
// Compute the model view matrix for the object rendered with GLKit
GLKMatrix4 modelViewMatrix = GLKMatrix4MakeTranslation(0.0f, 0.0f, -1.5f);
modelViewMatrix = GLKMatrix4Rotate(modelViewMatrix, 0.f, 1.0f, 1.0f, 1.0f);
modelViewMatrix = GLKMatrix4Multiply(baseModelViewMatrix, modelViewMatrix);
self.effect.transform.modelviewMatrix = modelViewMatrix;
}
- (void)tearDownGL
{
[EAGLContext setCurrentContext:self.context];
[_cube tearDownGL];
self.effect = nil;
if (_program) {
glDeleteProgram(_program);
_program = 0;
}
}
#pragma mark - GLKView and GLKViewController delegate methods
- (void)update
{
[_cube rotateInPlane:Plane{0,1} withAngle:0.040f]; // xy plane
[_cube rotateInPlane:Plane{0,2} withAngle:0.030f]; // xz plane
[_cube rotateInPlane:Plane{1,2} withAngle:0.010f]; // yz plane
}
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
glClearColor(0.65f, 0.65f, 0.65f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
[_cube prepareToRender];
[self.effect prepareToDraw];
[_cube render];
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment