Last active
August 29, 2015 14:08
-
-
Save membersheep/22db1a2a6b794b4fe1d7 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// SXBezierCurve.h | |
// Klee | |
// | |
// Created by Alessandro Maroso on 04/11/14. | |
// Copyright (c) 2014 waresme. All rights reserved. | |
// | |
// For informations about bezier curves and how to configure them with control points: http://pomax.github.io/bezierinfo/ | |
// | |
#import <Sparrow/Sparrow.h> | |
@class SPBaseEffect; | |
@class SPVertexData; | |
typedef enum | |
{ | |
SXBezierCurveTypeLinear, | |
SXBezierCurveTypeQuadratic, | |
SXBezierCurveTypeCubic | |
} SXBezierCurveType; | |
@interface SXBezierCurve : SPDisplayObject | |
{ | |
SPBaseEffect *_baseEffect; | |
SPVertexData *_vertexData; | |
uint _vertexBufferName; | |
BOOL _updateRequired; | |
} | |
/// Bezier curve type | |
@property (nonatomic, assign) SXBezierCurveType curveType; | |
/// Starting point | |
@property (nonatomic, strong) SPPoint *startPoint; | |
/// Ending point | |
@property (nonatomic, strong) SPPoint *endPoint; | |
/// First control point | |
@property (nonatomic, strong) SPPoint *controlPoint1; | |
/// Second control point | |
@property (nonatomic, strong) SPPoint *controlPoint2; | |
/// Color of the line | |
@property (nonatomic, assign) uint color; | |
/// Thickness of the line in pixels | |
@property (nonatomic, assign) float thickness; | |
/// Number of segments that are composing the cruved line (default = 100) | |
@property (nonatomic, assign) uint segmentsNumber; | |
/// The first and last control points are always the end points of the curve; however, the intermediate control points (if any) generally do not lie on the curve. Default cubic. | |
- (SXBezierCurve *)initWithStartPoint:(SPPoint*)start endPoint:(SPPoint*)end controlPoint1:(SPPoint*)c1 controlPoint2:(SPPoint*)c2; | |
/// The point at t of the Bézier curve (x component) | |
- (float)bezierX:(float)t; | |
/// The point at t of the Bézier curve (y component) | |
- (float)bezierY:(float)t; | |
/// The point at t of the Bézier curve | |
- (SPPoint*)bezier:(float)t; | |
/// The point at t of the derivative of the Bézier curve with respect to t (x component) | |
- (float)bezierTangentX:(float)t; | |
/// The point at t of the derivative of the Bézier curve with respect to t (y component) | |
- (float)bezierTangentY:(float)t; | |
/// The point at t of the derivative of the Bézier curve with respect to t (x component) | |
- (SPPoint*)bezierTangent:(float)t; | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// SXBezierCurve.m | |
// Klee | |
// | |
// Created by Alessandro Maroso on 04/11/14. | |
// Copyright (c) 2014 waresme. All rights reserved. | |
// | |
// A Bezier curve is separable, that means that you can compute it one coordinate at a time (first x, then y, then z...). | |
// | |
#import "SXBezierCurve.h" | |
@implementation SXBezierCurve | |
- (SXBezierCurve *)initWithStartPoint:(SPPoint*)start endPoint:(SPPoint*)end controlPoint1:(SPPoint*)c1 controlPoint2:(SPPoint*)c2 | |
{ | |
if (self = [super init]) { | |
self.curveType = SXBezierCurveTypeCubic; | |
self.startPoint = start; | |
self.endPoint = end; | |
self.controlPoint1 = c1; | |
self.controlPoint2 = c2; | |
self.segmentsNumber = 100; | |
self.thickness = 1; | |
_baseEffect = [[SPBaseEffect alloc] init]; | |
_vertexData = [[SPVertexData alloc] initWithSize:self.segmentsNumber premultipliedAlpha:YES]; | |
for (int i = 0 ; i < _vertexData.numVertices; i++) | |
_vertexData.vertices[i].color = SPVertexColorMakeWithColorAndAlpha(SPColorRed, 1.0f); | |
[self setupVertices]; | |
_updateRequired = YES; | |
} | |
return self; | |
} | |
#pragma mark - OpenGL methods | |
- (void)update | |
{ | |
[self setupVertices]; | |
[self syncBuffers]; | |
_updateRequired = NO; | |
} | |
/// Setup vertex data to bind to opengl | |
- (void)setupVertices | |
{ | |
// Set up vertex data positions basing on bezier function | |
for (int i = 0; i < _vertexData.numVertices; i++) { | |
[_vertexData setPositionWithX:[self bezierX:(float)i/(float)_vertexData.numVertices] y:[self bezierY:(float)i/(float)_vertexData.numVertices] atIndex:i]; | |
} | |
} | |
- (float)bezierX:(float)t | |
{ | |
float nt = 1.0f - t; | |
if (self.curveType == SXBezierCurveTypeCubic) | |
return self.startPoint.x * nt * nt * nt + 3.0 * self.controlPoint1.x * nt * nt * t + 3.0 * self.controlPoint2.x * nt * t * t + self.endPoint.x * t * t * t; | |
else if (self.curveType == SXBezierCurveTypeQuadratic) | |
return self.startPoint.x * nt * nt + 2.0 * self.controlPoint1.x * nt * t + self.endPoint.x * t * t; | |
else | |
return self.startPoint.x * nt + self.endPoint.x * t; | |
} | |
- (float)bezierY:(float)t | |
{ | |
float nt = 1.0f - t; | |
if (self.curveType == SXBezierCurveTypeCubic) | |
return self.startPoint.y * nt * nt * nt + 3.0 * self.controlPoint1.y * nt * nt * t + 3.0 * self.controlPoint2.y * nt * t * t + self.endPoint.y * t * t * t; | |
else if (self.curveType == SXBezierCurveTypeQuadratic) | |
return self.startPoint.y * nt * nt + 2.0 * self.controlPoint1.y * nt * t + self.endPoint.y * t * t; | |
else | |
return self.startPoint.y * nt + self.endPoint.y * t; | |
} | |
- (SPPoint*)bezier:(float)t | |
{ | |
return [SPPoint pointWithX:[self bezierX:t] y:[self bezierY:t]]; | |
} | |
- (float)bezierTangentX:(float)t | |
{ | |
float nt = 1.0f - t; | |
return -3.0 * self.startPoint.x * nt * nt + 3.0 * self.controlPoint1.x * (1.0 - 4.0 * t + 3.0 * t * t) + 3.0 * self.controlPoint2.x * (2.0 * t - 3.0 * t * t) + 3.0 * self.endPoint.x * t * t; | |
} | |
- (float)bezierTangentY:(float)t | |
{ | |
float nt = 1.0f - t; | |
return -3.0 * self.startPoint.y * nt * nt + 3.0 * self.controlPoint1.y * (1.0 - 4.0 * t + 3.0 * t * t) + 3.0 * self.controlPoint2.y * (2.0 * t - 3.0 * t * t) + 3.0 * self.endPoint.y * t * t; | |
} | |
- (SPPoint*)bezierTangent:(float)t | |
{ | |
return [SPPoint pointWithX:[self bezierTangentX:t] y:[self bezierTangentY:t]]; | |
} | |
- (void)syncBuffers | |
{ | |
if (!_vertexBufferName) { | |
[self createBuffers]; | |
} | |
// Bind the buffer object array to the GL_ARRAY_BUFFER target buffer | |
glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferName); | |
// Send the line data over to the target buffer in GPU RAM | |
glBufferData(GL_ARRAY_BUFFER, // the target buffer | |
sizeof(SPVertex) * _vertexData.numVertices, // the number of bytes to put into the buffer | |
_vertexData.vertices, // a pointer to the data being copied | |
GL_STATIC_DRAW); // the usage pattern of the data | |
} | |
- (void)createBuffers { | |
// First, we destory the existing buffers. | |
[self destroyBuffers]; | |
// Have OpenGL generate a (1) buffer name and store it in the buffer object array (_vertexBufferName) | |
glGenBuffers(1, &_vertexBufferName); | |
_updateRequired = YES; | |
} | |
- (void)destroyBuffers { | |
if (_vertexBufferName) { | |
glDeleteBuffers(1, &_vertexBufferName); | |
_vertexBufferName = 0; | |
} | |
} | |
/// Renders the display object with the help of a support object. | |
- (void)render:(SPRenderSupport *)support | |
{ | |
if (_updateRequired) | |
[self update]; | |
[support finishQuadBatch]; // finish previously batched quads | |
[support addDrawCalls:1]; // update stats display | |
_baseEffect.mvpMatrix = support.mvpMatrix; | |
_baseEffect.alpha = support.alpha; | |
[_baseEffect prepareToDraw]; | |
[SPBlendMode applyBlendFactorsForBlendMode:support.blendMode | |
premultipliedAlpha:_vertexData.premultipliedAlpha]; | |
int attribPosition = _baseEffect.attribPosition; | |
int attribColor = _baseEffect.attribColor; | |
glEnableVertexAttribArray(attribPosition); | |
glEnableVertexAttribArray(attribColor); | |
glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferName); | |
glVertexAttribPointer(attribPosition, 2, GL_FLOAT, GL_FALSE, sizeof(SPVertex), (void *)(offsetof(SPVertex, position))); | |
glVertexAttribPointer(attribColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(SPVertex), (void *)(offsetof(SPVertex, color))); | |
glLineWidth(self.thickness); | |
glDrawArrays(GL_LINE_STRIP, 0, _vertexData.numVertices); | |
} | |
#pragma mark - Setters/Getters | |
- (uint)color { | |
return self.color; | |
} | |
- (void)setColor:(uint)color { | |
[_vertexData setColor:color]; | |
_updateRequired = YES; | |
} | |
- (void)setStartPoint:(SPPoint *)startPoint | |
{ | |
_startPoint = startPoint; | |
_updateRequired = YES; | |
} | |
- (void)setEndPoint:(SPPoint *)endPoint | |
{ | |
_endPoint = endPoint; | |
_updateRequired = YES; | |
} | |
- (void)setControlPoint1:(SPPoint *)controlPoint1 | |
{ | |
_controlPoint1 = controlPoint1; | |
_updateRequired = YES; | |
} | |
- (void)setControlPoint2:(SPPoint *)controlPoint2 | |
{ | |
_controlPoint2 = controlPoint2; | |
_updateRequired = YES; | |
} | |
#pragma mark - Object methods | |
- (SPRectangle*)boundsInSpace:(SPDisplayObject*)targetSpace | |
{ | |
if (_updateRequired) | |
[self update]; | |
if (!targetSpace) | |
targetSpace = self; | |
SPMatrix *matrix = [self transformationMatrixToSpace:targetSpace]; | |
return [_vertexData boundsAfterTransformation:matrix]; | |
} | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// SXTexturedBezierCurve.h | |
// Klee | |
// | |
// Created by Alessandro Maroso on 10/11/14. | |
// Copyright (c) 2014 waresme. All rights reserved. | |
// | |
#import <Sparrow/Sparrow.h> | |
#import "SXBezierCurve.h" | |
@interface SXTexturedBezierCurve : SXBezierCurve | |
{ | |
SPTexture *_texture; | |
uint _indexBufferName; | |
ushort *_indexData; | |
} | |
- (SXTexturedBezierCurve *)initWithStartPoint:(SPPoint*)start endPoint:(SPPoint*)end controlPoint1:(SPPoint*)c1 controlPoint2:(SPPoint*)c2 texture:(SPTexture*)texture; | |
@property (nonatomic, strong) SPTexture *texture; | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// SXTexturedBezierCurve.m | |
// Klee | |
// | |
// Created by Alessandro Maroso on 10/11/14. | |
// Copyright (c) 2014 waresme. All rights reserved. | |
// | |
#import "SXTexturedBezierCurve.h" | |
@implementation SXTexturedBezierCurve | |
- (SXTexturedBezierCurve *)initWithStartPoint:(SPPoint*)start endPoint:(SPPoint*)end controlPoint1:(SPPoint*)c1 controlPoint2:(SPPoint*)c2 texture:(SPTexture*)texture | |
{ | |
if (self = [super init]) | |
{ | |
self.curveType = SXBezierCurveTypeCubic; | |
self.startPoint = start; | |
self.endPoint = end; | |
self.controlPoint1 = c1; | |
self.controlPoint2 = c2; | |
self.segmentsNumber = 100; | |
self.thickness = 10; | |
self.texture = texture; | |
self.texture.repeat = YES; | |
_baseEffect = [[SPBaseEffect alloc] init]; | |
[self setupVertices]; | |
_updateRequired = YES; | |
} | |
return self; | |
} | |
#pragma mark - OpenGL methods | |
- (void)update | |
{ | |
[self setupVertices]; | |
[self syncBuffers]; | |
_updateRequired = NO; | |
} | |
/// Setup vertex data to bind to opengl | |
- (void)setupVertices | |
{ | |
// Create vertex data object | |
_vertexData = [[SPVertexData alloc] initWithSize:self.segmentsNumber*2+2 | |
premultipliedAlpha:YES]; | |
for (int i = 0 ; i < _vertexData.numVertices; i++) | |
_vertexData.vertices[i].color = SPVertexColorMakeWithColorAndAlpha(SPColorWhite, 1.0f); | |
// Set up vertex data positions basing on bezier function at each point | |
SPPoint *bezierPoint, *previousBezierPoint, *perpDirection; | |
float currentLength = 0; | |
for (int i = 0; i <= self.segmentsNumber; i++) | |
{ | |
// Calculate bezier point | |
if (bezierPoint) | |
previousBezierPoint = bezierPoint; | |
bezierPoint = [self bezier:(float)i/(float)self.segmentsNumber]; | |
if (previousBezierPoint) | |
currentLength += [bezierPoint subtractPoint:previousBezierPoint].length; | |
// Find tangent at that point, get perpendicular vector, normalize it and scale it by thickness factor | |
perpDirection = [[[[self bezierTangent:(float)i/(float)self.segmentsNumber] perpendicular] normalize] scaleBy:self.thickness]; | |
// Set vertices positions | |
[_vertexData setPosition:[bezierPoint subtractPoint:perpDirection] | |
atIndex:i*2]; | |
[_vertexData setPosition:[bezierPoint addPoint:perpDirection] | |
atIndex:i*2+1]; | |
// One texture for all the line | |
// Set up the texture coordinates of each vertex. Texture coordinates are in the range [0, 1]. | |
[_vertexData setTexCoordsWithX:currentLength/self.texture.width | |
y:0 | |
atIndex:i*2+1]; | |
[_vertexData setTexCoordsWithX:currentLength/self.texture.width | |
y:1 | |
atIndex:i*2]; | |
} | |
[_texture adjustVertexData:_vertexData atIndex:0 numVertices:_vertexData.numVertices]; | |
// Set up Index data for triangle strip drawing | |
int numIndices = self.segmentsNumber * 2 + 2; | |
if (!_indexData) | |
_indexData = malloc(sizeof(ushort) * numIndices); | |
else | |
_indexData = realloc(_indexData, sizeof(ushort) * numIndices); | |
// Vertices positions | |
// 1---3---5 | |
// | \ | \ | (...) | |
// 0---2---4 | |
for (int i = 0; i <= self.segmentsNumber; i++) | |
{ | |
_indexData[i*2] = i*2; | |
_indexData[i*2+1] = i*2+1; | |
} | |
} | |
- (void)destroyBuffers | |
{ | |
if (_vertexBufferName) | |
{ | |
glDeleteBuffers(1, &_vertexBufferName); | |
_vertexBufferName = 0; | |
} | |
if (_indexBufferName) | |
{ | |
glDeleteBuffers(1, &_indexBufferName); | |
_indexBufferName = 0; | |
} | |
} | |
- (void)createBuffers | |
{ | |
// First, we destory the existing buffers. | |
[self destroyBuffers]; | |
// Have OpenGL generate buffer names and store them in the buffer object array | |
glGenBuffers(1, &_vertexBufferName); | |
glGenBuffers(1, &_indexBufferName); | |
if (!_vertexBufferName || !_indexBufferName) | |
[NSException raise:SP_EXC_OPERATION_FAILED format:@"could not create vertex buffers"]; | |
} | |
- (void)syncBuffers | |
{ | |
if (!_vertexBufferName || !_indexBufferName) | |
[self createBuffers]; | |
// Bind the buffer object array to the GL_ARRAY_BUFFER target buffer | |
glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferName); | |
// Send the data over to the target buffer in GPU RAM | |
glBufferData(GL_ARRAY_BUFFER, sizeof(SPVertex) * _vertexData.numVertices, _vertexData.vertices, GL_DYNAMIC_DRAW); | |
int numIndices = self.segmentsNumber * 2 + 2; | |
// Bind the buffer object array to the GL_ELEMENT_ARRAY_BUFFER target buffer | |
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBufferName); | |
// Send the data over to the target buffer in GPU RAM | |
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(ushort) * numIndices, _indexData, GL_DYNAMIC_DRAW); | |
} | |
/// Renders the display object with the help of a support object. | |
- (void)render:(SPRenderSupport *)support | |
{ | |
if (_updateRequired) | |
[self update]; | |
[support finishQuadBatch]; // finish previously batched quads | |
[support addDrawCalls:1]; // update stats display | |
_baseEffect.mvpMatrix = support.mvpMatrix; | |
_baseEffect.alpha = support.alpha; | |
_baseEffect.texture = _texture; | |
[_baseEffect prepareToDraw]; | |
[SPBlendMode applyBlendFactorsForBlendMode:support.blendMode | |
premultipliedAlpha:_vertexData.premultipliedAlpha]; | |
// Vertex Shader program input attributes (create names-indices) | |
int attribPosition = _baseEffect.attribPosition; | |
int attribColor = _baseEffect.attribColor; | |
int attribTexCoords = _baseEffect.attribTexCoords; | |
glEnableVertexAttribArray(attribPosition); | |
glEnableVertexAttribArray(attribColor); | |
glEnableVertexAttribArray(attribTexCoords); | |
// Bind this object buffers object array to the opengl vertex and index buffers | |
glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferName); | |
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBufferName); | |
// Specify the location and data format of the arrays of vertex shader program input attributes to use when rendering | |
glVertexAttribPointer(attribPosition, 2, GL_FLOAT, GL_FALSE, sizeof(SPVertex), (void *)(offsetof(SPVertex, position))); | |
glVertexAttribPointer(attribColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(SPVertex), (void *)(offsetof(SPVertex, color))); | |
if (self.texture) | |
glVertexAttribPointer(attribTexCoords, 2, GL_FLOAT, GL_FALSE, sizeof(SPVertex), (void *)(offsetof(SPVertex, texCoords))); | |
int numIndices = self.segmentsNumber * 2 + 2; | |
glDrawElements(GL_TRIANGLE_STRIP, numIndices, GL_UNSIGNED_SHORT, 0); | |
} | |
#pragma mark - Setters/Getters | |
- (void)setTexture:(SPTexture *)value | |
{ | |
if (value == nil) | |
{ | |
[NSException raise:SP_EXC_INVALID_OPERATION format:@"texture cannot be nil!"]; | |
} | |
else if (value != _texture) | |
{ | |
_texture = value; | |
_texture.repeat = YES; | |
_updateRequired = YES; | |
[_vertexData setPremultipliedAlpha:_texture.premultipliedAlpha updateVertices:YES]; | |
} | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment