其中關於各種3D Model的模型,可以參考網址
http://www.math.oregonstate.edu/home/programs/undergrad/CalculusQuestStudyGuides/vcalc/parsurf/parsurf.html
1. 重新開啓一個專案
2. 依次加入新的檔案,目錄如下,先加入所需的QuartzCore及OpenGLES的FrameWork
3. 照目錄上的順序,加入ParametricEquations.h這隻檔案,目的是提供六個3D Model的極坐標計算方程。最後一個是自己根據作法稍加修改作為測試之用。
#include "ParametricSurface.h"class Cone : public ParametricSurface {
public:
Cone(float height, float radius) : m_height(height), m_radius(radius)
{
ParametricInterval interval = { ivec2(20, 20), vec2(TwoPi, 1), vec2(30, 20) };
SetInterval(interval);
}
vec3 Evaluate(const vec2& domain) const
{
float u = domain.x, v = domain.y;
float x = m_radius * (1 - v) * cos(u);
float y = m_height * (v - 0.5f);
float z = m_radius * (1 - v) * -sin(u);
return vec3(x, y, z);
}
private:
float m_height;
float m_radius;
};
'
// cone原始數學等效式
// 參考 http://www.math.oregonstate.edu/home/programs/undergrad/CalculusQuestStudyGuides/vcalc/parsurf/parsurf.html
//後面的可以如法參考
class Sphere : public ParametricSurface {
public:
Sphere(float radius) : m_radius(radius)
{
ParametricInterval interval = { ivec2(20, 20), vec2(Pi, TwoPi), vec2(20, 35) };
SetInterval(interval);
}
vec3 Evaluate(const vec2& domain) const
{
float u = domain.x, v = domain.y;
float x = m_radius * sin(u) * cos(v);
float y = m_radius * cos(u);
float z = m_radius * -sin(u) * sin(v);
return vec3(x, y, z); // 求取極坐標
}
private:
float m_radius;
};
// sphere原始數學等效式
class Torus : public ParametricSurface {
public:
Torus(float majorRadius, float minorRadius) :
m_majorRadius(majorRadius),
m_minorRadius(minorRadius)
{
ParametricInterval interval = { ivec2(20, 20), vec2(TwoPi, TwoPi), vec2(40, 10) };
SetInterval(interval);
}
vec3 Evaluate(const vec2& domain) const
{
const float major = m_majorRadius;
const float minor = m_minorRadius;
float u = domain.x, v = domain.y;
float x = (major + minor * cos(v)) * cos(u);
float y = (major + minor * cos(v)) * sin(u);
float z = minor * sin(v);
return vec3(x, y, z);
}
private:
float m_majorRadius;
float m_minorRadius;
};
class TrefoilKnot : public ParametricSurface {
public:
TrefoilKnot(float scale) : m_scale(scale)
{
ParametricInterval interval = { ivec2(60, 15), vec2(TwoPi, TwoPi), vec2(100, 8) };
SetInterval(interval);
}
vec3 Evaluate(const vec2& domain) const
{
const float a = 0.5f;
const float b = 0.3f;
const float c = 0.5f;
const float d = 0.1f;
float u = (TwoPi - domain.x) * 2;
float v = domain.y;
float r = a + b * cos(1.5f * u);
float x = r * cos(u);
float y = r * sin(u);
float z = c * sin(1.5f * u);
vec3 dv;
dv.x = -1.5f * b * sin(1.5f * u) * cos(u) -
(a + b * cos(1.5f * u)) * sin(u);
dv.y = -1.5f * b * sin(1.5f * u) * sin(u) +
(a + b * cos(1.5f * u)) * cos(u);
dv.z = 1.5f * c * cos(1.5f * u);
vec3 q = dv.Normalized();
vec3 qvn = vec3(q.y, -q.x, 0).Normalized();
vec3 ww = q.Cross(qvn);
vec3 range;
range.x = x + d * (qvn.x * cos(v) + ww.x * sin(v));
range.y = y + d * (qvn.y * cos(v) + ww.y * sin(v));
range.z = z + d * ww.z * sin(v);
return range * m_scale;
}
private:
float m_scale;
};
class MobiusStrip : public ParametricSurface {
public:
MobiusStrip(float scale) : m_scale(scale)
{
ParametricInterval interval = { ivec2(40, 20), vec2(TwoPi, TwoPi), vec2(40, 15) };
SetInterval(interval);
}
vec3 Evaluate(const vec2& domain) const
{
float u = domain.x;
float t = domain.y;
float major = 1.25;
float a = 0.125f;
float b = 0.5f;
float phi = u / 2;
// General equation for an ellipse where phi is the angle
// between the major axis and the X axis.
float x = a * cos(t) * cos(phi) - b * sin(t) * sin(phi);
float y = a * cos(t) * sin(phi) + b * sin(t) * cos(phi);
// Sweep the ellipse along a circle, like a torus.
vec3 range;
range.x = (major + x) * cos(u);
range.y = (major + x) * sin(u);
range.z = y;
return range * m_scale;
}
private:
float m_scale;
};
class KleinBottle : public ParametricSurface {
public:
KleinBottle(float scale) : m_scale(scale)
{
ParametricInterval interval = { ivec2(20, 20), vec2(TwoPi, TwoPi), vec2(15, 50) };
SetInterval(interval);
}
vec3 Evaluate(const vec2& domain) const
{
float v = 1 - domain.x;
float u = domain.y;
float x0 = 3 * cos(u) * (1 + sin(u)) +
(2 * (1 - cos(u) / 2)) * cos(u) * cos(v);
float y0 = 8 * sin(u) + (2 * (1 - cos(u) / 2)) * sin(u) * cos(v);
float x1 = 3 * cos(u) * (1 + sin(u)) +
(2 * (1 - cos(u) / 2)) * cos(v + Pi);
float y1 = 8 * sin(u);
vec3 range;
range.x = u < Pi ? x0 : x1;
range.y = u < Pi ? -y0 : -y1;
range.z = (-2 * (1 - cos(u) / 2)) * sin(v);
return range * m_scale;
}
bool InvertNormal(const vec2& domain) const
{
return domain.y > 3 * Pi / 2;
}
private:
float m_scale;
};
class KirTest : public ParametricSurface {
public:
KirTest(float scale) : m_scale(scale)
{
ParametricInterval interval = { ivec2(20, 30), vec2(TwoPi, TwoPi), vec2(5, 30) };
SetInterval(interval);
}
vec3 Evaluate(const vec2& domain) const
{
float v = 1 - domain.x;
float u = domain.y;
float x0 = 3 * cos(u) * (1 + sin(u)) +
(2 * (1 - cos(u) / 2)) * cos(u) * cos(v);
float y0 = 8 * sin(u) + (2 * (1 - cos(u) / 2)) * sin(u) * cos(v);
float x1 = 3 * cos(u) * (1 + sin(u)) +
(2 * (1 - cos(u) / 2)) * cos(v + Pi);
float y1 = 8 * cos(u);
vec3 range;
range.x = u < Pi ? x0 : x1;
range.y = u < Pi ? -y0 : -y1;
range.z = (-2 * (1 - cos(u) / 2)) * sin(v);
return range * m_scale;
}
bool InvertNormal(const vec2& domain) const
{
return domain.y > 3 * Pi / 2;
}
private:
float m_scale;
};
4. ParametricSurface.cpp 產生Vertices & Indices的程式區
#include "ParametricSurface.h"void ParametricSurface::SetInterval(const ParametricInterval& interval)
{
m_divisions = interval.Divisions;
m_upperBound = interval.UpperBound;
m_textureCount = interval.TextureCount;
m_slices = m_divisions - ivec2(1, 1);
}
void ParametricSurface::SetVertexFlags(unsigned char flags)
{
m_vertexFlags = flags;
}
int ParametricSurface::GetVertexSize() const
{
int floatsPerVertex = 3;
if (m_vertexFlags & VertexFlagsNormals)
floatsPerVertex += 3;
if (m_vertexFlags & VertexFlagsTexCoords)
floatsPerVertex += 2;
return floatsPerVertex;
}
int ParametricSurface::GetVertexCount() const
{
return m_divisions.x * m_divisions.y;
}
int ParametricSurface::GetLineIndexCount() const
{
return 4 * m_slices.x * m_slices.y;
}
int ParametricSurface::GetTriangleIndexCount() const
{
return 6 * m_slices.x * m_slices.y;
}
vec2 ParametricSurface::ComputeDomain(float x, float y) const
{
return vec2(x * m_upperBound.x / m_slices.x, y * m_upperBound.y / m_slices.y);
}
void ParametricSurface::GenerateVertices(float * vertices) const
{
float* attribute = vertices;
for (int j = 0; j < m_divisions.y; j++) {
for (int i = 0; i < m_divisions.x; i++) {
// Compute Position
vec2 domain = ComputeDomain(i, j);
vec3 range = Evaluate(domain); // 求取極座標
attribute = range.Write(attribute);
/* 此區可用可不用,怪怪的,有時會影響結果,但不固定????
// 暫時不知道何處讓m_vertexFlags = 1,並不固定???,沒有一定的規則。
int test = m_vertexFlags;
int test1 = VertexFlagsNormals;
int test2 = m_vertexFlags & VertexFlagsNormals;
// Compute Normal // 表面的法向量
if (m_vertexFlags & VertexFlagsNormals) { // 暫時不知道何處讓m_vertexFlags = 1,並不固定???
float s = i, t = j;
// Nudge the point if the normal is indeterminate.
if (i == 0) s += 0.01f;
if (i == m_divisions.x - 1) s -= 0.01f;
if (j == 0) t += 0.01f;
if (j == m_divisions.y - 1) t -= 0.01f;
// Compute the tangents and their cross product.
vec3 p = Evaluate(ComputeDomain(s, t));
vec3 u = Evaluate(ComputeDomain(s + 0.01f, t)) - p;
vec3 v = Evaluate(ComputeDomain(s, t + 0.01f)) - p;
vec3 normal = u.Cross(v).Normalized();
if (InvertNormal(domain))
normal = -normal;
attribute = normal.Write(attribute);
}
int test3 = m_vertexFlags & VertexFlagsTexCoords;
// Compute Texture Coordinates 計算紋理坐標
if (m_vertexFlags & VertexFlagsTexCoords) {
float s = m_textureCount.x * i / m_slices.x;
float t = m_textureCount.y * j / m_slices.y;
attribute = vec2(s, t).Write(attribute);
}
*/
}
}
}
void ParametricSurface::GenerateLineIndices(unsigned short * indices) const
{
unsigned short * index = indices;
for (int j = 0, vertex = 0; j < m_slices.y; j++) {
for (int i = 0; i < m_slices.x; i++) {
int next = (i + 1) % m_divisions.x;
*index++ = vertex + i;
*index++ = vertex + next;
*index++ = vertex + i;
*index++ = vertex + i + m_divisions.x;
}
vertex += m_divisions.x;
}
}
void ParametricSurface::GenerateTriangleIndices(unsigned short * indices) const
{
unsigned short * index = indices;
for (int j = 0, vertex = 0; j < m_slices.y; j++) {
for (int i = 0; i < m_slices.x; i++) {
int next = (i + 1) % m_divisions.x;
*index++ = vertex + i;
*index++ = vertex + next;
*index++ = vertex + i + m_divisions.x;
*index++ = vertex + next;
*index++ = vertex + next + m_divisions.x;
*index++ = vertex + i + m_divisions.x;
}
vertex += m_divisions.x;
}
}
5. ParametricSurface.h前一個程式檔的定義區
#pragma once#include "Vector.h"
enum VertexFlags
{
VertexFlagsNormals = 1 << 0, // = 1
VertexFlagsTexCoords = 1 << 1, // = 2
};
struct ISurface
{
virtual ~ISurface() {}
virtual void SetVertexFlags(unsigned char flags = 0) = 0;
virtual int GetVertexSize() const = 0;
virtual int GetVertexCount() const = 0;
virtual int GetLineIndexCount() const = 0;
virtual int GetTriangleIndexCount() const = 0;
virtual void GenerateVertices(float * vertices) const = 0;
virtual void GenerateLineIndices(unsigned short * indices) const = 0;
virtual void GenerateTriangleIndices(unsigned short * indices) const = 0;
};
struct ParametricInterval
{
ivec2 Divisions;
vec2 UpperBound;
vec2 TextureCount;
};
class ParametricSurface : public ISurface
{
public:
void SetVertexFlags(unsigned char flags = 0);
int GetVertexSize() const;
int GetVertexCount() const;
int GetLineIndexCount() const;
int GetTriangleIndexCount() const;
void GenerateVertices(float * vertices) const;
void GenerateLineIndices(unsigned short * indices) const;
void GenerateTriangleIndices(unsigned short * indices) const;
//void drawCube(KSColor color);
void drawCube();
protected:
void SetInterval(const ParametricInterval& interval);
virtual vec3 Evaluate(const vec2& domain) const = 0;
virtual bool InvertNormal(const vec2& domain) const { return false; }
private:
vec2 ComputeDomain(float i, float j) const;
ivec2 m_slices;
ivec2 m_divisions;
vec2 m_upperBound;
vec2 m_textureCount;
unsigned char m_vertexFlags;
};
6. 進入到Utils區的第一個檔案Vector.h,計算Vector相關的程式,使用Template方式寫的。
#pragma once
#include "GLESMath.h"
const float Pi = M_PI;
const float TwoPi = 2 * M_PI;
template <typename T>
struct Vector2
{
Vector2() {}
Vector2(T x, T y) : x(x), y(y) {}
T Dot(const Vector2& v) const
{
return x * v.x + y * v.y;
}
Vector2 operator+(const Vector2& v) const
{
return Vector2(x + v.x, y + v.y);
}
Vector2 operator-(const Vector2& v) const
{
return Vector2(x - v.x, y - v.y);
}
Vector2 operator/(float s) const
{
return Vector2(x / s, y / s);
}
Vector2 operator*(float s) const
{
return Vector2(x * s, y * s);
}
void Normalize()
{
float s = 1.0f / Length();
x *= s;
y *= s;
}
Vector2 Normalized() const
{
Vector2 v = *this;
v.Normalize();
return v;
}
T LengthSquared() const
{
return x * x + y * y;
}
T Length() const
{
return sqrt(LengthSquared());
}
operator Vector2<float>() const
{
return Vector2<float>(x, y);
}
bool operator==(const Vector2& v) const
{
return x == v.x && y == v.y;
}
Vector2 Lerp(float t, const Vector2& v) const
{
return Vector2(x * (1 - t) + v.x * t,
y * (1 - t) + v.y * t);
}
template <typename P>
P* Write(P* pData)
{
Vector2* pVector = (Vector2*) pData;
*pVector++ = *this;
return (P*) pVector;
}
T x;
T y;
};
template <typename T>
struct Vector3
{
Vector3() {}
Vector3(T x, T y, T z) : x(x), y(y), z(z) {}
void Normalize()
{
float s = 1.0f / sqrt(x * x + y * y + z * z);
x *= s;
y *= s;
z *= s;
}
Vector3 Normalized() const
{
Vector3 v = *this;
v.Normalize();
return v;
}
Vector3 Cross(const Vector3& v) const
{
return Vector3(y * v.z - z * v.y,
z * v.x - x * v.z,
x * v.y - y * v.x);
}
T Dot(const Vector3& v) const
{
return x * v.x + y * v.y + z * v.z;
}
Vector3 operator+(const Vector3& v) const
{
return Vector3(x + v.x, y + v.y, z + v.z);
}
void operator+=(const Vector3& v)
{
x += v.x;
y += v.y;
z += v.z;
}
void operator-=(const Vector3& v)
{
x -= v.x;
y -= v.y;
z -= v.z;
}
void operator/=(T s)
{
x /= s;
y /= s;
z /= s;
}
Vector3 operator-(const Vector3& v) const
{
return Vector3(x - v.x, y - v.y, z - v.z);
}
Vector3 operator-() const
{
return Vector3(-x, -y, -z);
}
Vector3 operator*(T s) const
{
return Vector3(x * s, y * s, z * s);
}
Vector3 operator/(T s) const
{
return Vector3(x / s, y / s, z / s);
}
bool operator==(const Vector3& v) const
{
return x == v.x && y == v.y && z == v.z;
}
Vector3 Lerp(float t, const Vector3& v) const
{
return Vector3(x * (1 - t) + v.x * t,
y * (1 - t) + v.y * t,
z * (1 - t) + v.z * t);
}
const T* Pointer() const
{
return &x;
}
template <typename P>
P* Write(P* pData)
{
Vector3<T>* pVector = (Vector3<T>*) pData;
*pVector++ = *this;
return (P*) pVector;
}
T x;
T y;
T z;
};
template <typename T>
struct Vector4
{
Vector4() {}
Vector4(T x, T y, T z, T w) : x(x), y(y), z(z), w(w) {}
T Dot(const Vector4& v) const
{
return x * v.x + y * v.y + z * v.z + w * v.w;
}
Vector4 Lerp(float t, const Vector4& v) const
{
return Vector4(x * (1 - t) + v.x * t,
y * (1 - t) + v.y * t,
z * (1 - t) + v.z * t,
w * (1 - t) + v.w * t);
}
const T* Pointer() const
{
return &x;
}
T x;
T y;
T z;
T w;
};
typedef Vector2<bool> bvec2;
typedef Vector2<int> ivec2;
typedef Vector3<int> ivec3;
typedef Vector4<int> ivec4;
typedef Vector2<float> vec2;
typedef Vector3<float> vec3;
typedef Vector4<float> vec4;
7. Quaternion.h 四維矩陣的計算式,主要在單一矩陣的計算,使用inline方式寫的。
#pragma once#include "Vector.h"
#include "GLESMath.h"
struct Quaternion
{
float x;
float y;
float z;
float w;
Quaternion();
Quaternion(float x, float y, float z, float w);
Quaternion Slerp(float mu, const Quaternion& q) const;
Quaternion Rotated(const Quaternion& b) const;
Quaternion Scaled(float scale) const;
float Dot(const Quaternion& q) const;
void ToMatrix4(KSMatrix4 * m) const;
Vector4<float> ToVector() const;
void ToIdentity();
Quaternion operator-(const Quaternion& q) const;
Quaternion operator+(const Quaternion& q) const;
bool operator==(const Quaternion& q) const;
bool operator!=(const Quaternion& q) const;
void Normalize();
void Rotate(const Quaternion& q);
static Quaternion CreateFromVectors(const Vector3<float>& v0, const Vector3<float>& v1);
static Quaternion CreateFromAxisAngle(const Vector3<float>& axis, float radians);
};
inline Quaternion::Quaternion() : x(0), y(0), z(0), w(1)
{}
inline Quaternion::Quaternion(float x, float y, float z, float w) : x(x), y(y), z(z), w(w)
{}
inline void Quaternion::ToIdentity()
{
x = y = z = 0;
w = 1.0;
}
// Ken Shoemake's famous method.
inline Quaternion Quaternion::Slerp(float t, const Quaternion& v1) const
{
const float epsilon = 0.0005f;
float dot = Dot(v1);
if (dot > 1 - epsilon) {
Quaternion result = v1 + (*this - v1).Scaled(t);
result.Normalize();
return result;
}
if (dot < 0)
dot = 0;
if (dot > 1)
dot = 1;
float theta0 = acos(dot);
float theta = theta0 * t;
Quaternion v2 = (v1 - Scaled(dot));
v2.Normalize();
Quaternion q = Scaled(cos(theta)) + v2.Scaled(sin(theta));
q.Normalize();
return q;
}
inline Quaternion Quaternion::Rotated(const Quaternion& b) const
{
Quaternion q;
q.w = w * b.w - x * b.x - y * b.y - z * b.z;
q.x = w * b.x + x * b.w + y * b.z - z * b.y;
q.y = w * b.y + y * b.w + z * b.x - x * b.z;
q.z = w * b.z + z * b.w + x * b.y - y * b.x;
q.Normalize();
return q;
}
inline Quaternion Quaternion::Scaled(float s) const
{
return Quaternion(x * s, y * s, z * s, w * s);
}
inline float Quaternion::Dot(const Quaternion& q) const
{
return x * q.x + y * q.y + z * q.z + w * q.w;
}
inline void Quaternion::ToMatrix4(KSMatrix4 * result) const
{
const float s = 2;
float xs, ys, zs;
float wx, wy, wz;
float xx, xy, xz;
float yy, yz, zz;
xs = x * s; ys = y * s; zs = z * s;
wx = w * xs; wy = w * ys; wz = w * zs;
xx = x * xs; xy = x * ys; xz = x * zs;
yy = y * ys; yz = y * zs; zz = z * zs;
result->m[0][0] = 1 - (yy + zz);
result->m[0][1] = xy + wz;
result->m[0][2] = xz - wy;
result->m[0][3] = 0;
result->m[1][0] = xy - wz;
result->m[1][1] = 1 - (xx + zz);
result->m[1][2] = yz + wx;
result->m[1][3] = 0;
result->m[2][0] = xz + wy;
result->m[2][1] = yz - wx;
result->m[2][2]= 1 - (xx + yy);
result->m[2][3] = 0;
result->m[3][0] = 0;
result->m[3][1] = 0;
result->m[3][2] = 0;
result->m[3][3] = 1;
}
inline Vector4<float> Quaternion::ToVector() const
{
return Vector4<float>(x, y, z, w);
}
inline Quaternion Quaternion::operator-(const Quaternion& q) const
{
return Quaternion(x - q.x, y - q.y, z - q.z, w - q.w);
}
inline Quaternion Quaternion::operator+(const Quaternion& q) const
{
return Quaternion(x + q.x, y + q.y, z + q.z, w + q.w);
}
inline bool Quaternion::operator==(const Quaternion& q) const
{
return x == q.x && y == q.y && z == q.z && w == q.w;
}
inline bool Quaternion::operator!=(const Quaternion& q) const
{
return !(*this == q);
}
inline void Quaternion::Normalize()
{
*this = Scaled(1 / sqrt(Dot(*this)));
}
inline void Quaternion::Rotate(const Quaternion& q2)
{
Quaternion q;
Quaternion& q1 = *this;
q.w = q1.w * q2.w - q1.x * q2.x - q1.y * q2.y - q1.z * q2.z;
q.x = q1.w * q2.x + q1.x * q2.w + q1.y * q2.z - q1.z * q2.y;
q.y = q1.w * q2.y + q1.y * q2.w + q1.z * q2.x - q1.x * q2.z;
q.z = q1.w * q2.z + q1.z * q2.w + q1.x * q2.y - q1.y * q2.x;
q.Normalize();
*this = q;
}
// Compute the quaternion that rotates from a to b, avoiding numerical instability.
// Taken from "The Shortest Arc Quaternion" by Stan Melax in "Game Programming Gems".
//
inline Quaternion Quaternion::CreateFromVectors(const Vector3<float>& v0, const Vector3<float>& v1)
{
if (v0 == -v1)
return Quaternion::CreateFromAxisAngle(vec3(1, 0, 0), Pi);
Vector3<float> c = v0.Cross(v1);
float d = v0.Dot(v1);
float s = sqrt((1 + d) * 2);
Quaternion q;
q.x = c.x / s;
q.y = c.y / s;
q.z = c.z / s;
q.w = s / 2.0f;
return q;
}
inline Quaternion Quaternion::CreateFromAxisAngle(const Vector3<float>& axis, float radians)
{
Quaternion q;
q.w = cos(radians / 2);
q.x = q.y = q.z = sin(radians / 2);
q.x *= axis.x;
q.y *= axis.y;
q.z *= axis.z;
return q;
}
8. GLESMath.c,四維矩陣的計算,主要在兩個矩陣之間的計算。
#include "GLESMath.h"#include <stdlib.h>
#include <math.h>
void * memcpy(void *, const void *, size_t);
void * memset(void *, int, size_t);
//
// Matrix math utils
//
void ksScale(KSMatrix4 *result, GLfloat sx, GLfloat sy, GLfloat sz)
{
result->m[0][0] *= sx;
result->m[0][1] *= sx;
result->m[0][2] *= sx;
result->m[0][3] *= sx;
result->m[1][0] *= sy;
result->m[1][1] *= sy;
result->m[1][2] *= sy;
result->m[1][3] *= sy;
result->m[2][0] *= sz;
result->m[2][1] *= sz;
result->m[2][2] *= sz;
result->m[2][3] *= sz;
}
void ksTranslate(KSMatrix4 *result, GLfloat tx, GLfloat ty, GLfloat tz)
{
result->m[3][0] += (result->m[0][0] * tx + result->m[1][0] * ty + result->m[2][0] * tz);
result->m[3][1] += (result->m[0][1] * tx + result->m[1][1] * ty + result->m[2][1] * tz);
result->m[3][2] += (result->m[0][2] * tx + result->m[1][2] * ty + result->m[2][2] * tz);
result->m[3][3] += (result->m[0][3] * tx + result->m[1][3] * ty + result->m[2][3] * tz);
}
void ksRotate(KSMatrix4 *result, GLfloat angle, GLfloat x, GLfloat y, GLfloat z)
{
GLfloat sinAngle, cosAngle;
GLfloat mag = sqrtf(x * x + y * y + z * z);
sinAngle = sinf ( angle * M_PI / 180.0f );
cosAngle = cosf ( angle * M_PI / 180.0f );
if ( mag > 0.0f )
{
GLfloat xx, yy, zz, xy, yz, zx, xs, ys, zs;
GLfloat oneMinusCos;
KSMatrix4 rotMat;
x /= mag;
y /= mag;
z /= mag;
xx = x * x;
yy = y * y;
zz = z * z;
xy = x * y;
yz = y * z;
zx = z * x;
xs = x * sinAngle;
ys = y * sinAngle;
zs = z * sinAngle;
oneMinusCos = 1.0f - cosAngle;
rotMat.m[0][0] = (oneMinusCos * xx) + cosAngle;
rotMat.m[0][1] = (oneMinusCos * xy) - zs;
rotMat.m[0][2] = (oneMinusCos * zx) + ys;
rotMat.m[0][3] = 0.0F;
rotMat.m[1][0] = (oneMinusCos * xy) + zs;
rotMat.m[1][1] = (oneMinusCos * yy) + cosAngle;
rotMat.m[1][2] = (oneMinusCos * yz) - xs;
rotMat.m[1][3] = 0.0F;
rotMat.m[2][0] = (oneMinusCos * zx) - ys;
rotMat.m[2][1] = (oneMinusCos * yz) + xs;
rotMat.m[2][2] = (oneMinusCos * zz) + cosAngle;
rotMat.m[2][3] = 0.0F;
rotMat.m[3][0] = 0.0F;
rotMat.m[3][1] = 0.0F;
rotMat.m[3][2] = 0.0F;
rotMat.m[3][3] = 1.0F;
ksMatrixMultiply( result, &rotMat, result );
}
}
void ksMatrixMultiply(KSMatrix4 *result, const KSMatrix4 *srcA, const KSMatrix4 *srcB)
{
KSMatrix4 tmp;
int i;
for (i=0; i<4; i++)
{
tmp.m[i][0] = (srcA->m[i][0] * srcB->m[0][0]) +
(srcA->m[i][1] * srcB->m[1][0]) +
(srcA->m[i][2] * srcB->m[2][0]) +
(srcA->m[i][3] * srcB->m[3][0]) ;
tmp.m[i][1] = (srcA->m[i][0] * srcB->m[0][1]) +
(srcA->m[i][1] * srcB->m[1][1]) +
(srcA->m[i][2] * srcB->m[2][1]) +
(srcA->m[i][3] * srcB->m[3][1]) ;
tmp.m[i][2] = (srcA->m[i][0] * srcB->m[0][2]) +
(srcA->m[i][1] * srcB->m[1][2]) +
(srcA->m[i][2] * srcB->m[2][2]) +
(srcA->m[i][3] * srcB->m[3][2]) ;
tmp.m[i][3] = (srcA->m[i][0] * srcB->m[0][3]) +
(srcA->m[i][1] * srcB->m[1][3]) +
(srcA->m[i][2] * srcB->m[2][3]) +
(srcA->m[i][3] * srcB->m[3][3]) ;
}
memcpy(result, &tmp, sizeof(KSMatrix4));
}
void ksCopyMatrix4(KSMatrix4 * target, const KSMatrix4 * src)
{
memcpy(target, src, sizeof(KSMatrix4));
}
void ksMatrix4ToMatrix3(KSMatrix3 * t, const KSMatrix4 * src)
{
t->m[0][0] = src->m[0][0];
t->m[0][1] = src->m[0][1];
t->m[0][2] = src->m[0][2];
t->m[1][0] = src->m[1][0];
t->m[1][1] = src->m[1][1];
t->m[1][2] = src->m[1][2];
t->m[2][0] = src->m[2][0];
t->m[2][1] = src->m[2][1];
t->m[2][2] = src->m[2][2];
}
void ksMatrixLoadIdentity(KSMatrix4 *result)
{
memset(result, 0x0, sizeof(KSMatrix4));
result->m[0][0] = 1.0f;
result->m[1][1] = 1.0f;
result->m[2][2] = 1.0f;
result->m[3][3] = 1.0f;
}
void ksFrustum(KSMatrix4 *result, float left, float right, float bottom, float top, float nearZ, float farZ)
{
float deltaX = right - left;
float deltaY = top - bottom;
float deltaZ = farZ - nearZ;
KSMatrix4 frust;
if ( (nearZ <= 0.0f) || (farZ <= 0.0f) ||
(deltaX <= 0.0f) || (deltaY <= 0.0f) || (deltaZ <= 0.0f) )
return;
frust.m[0][0] = 2.0f * nearZ / deltaX;
frust.m[0][1] = frust.m[0][2] = frust.m[0][3] = 0.0f;
frust.m[1][1] = 2.0f * nearZ / deltaY;
frust.m[1][0] = frust.m[1][2] = frust.m[1][3] = 0.0f;
frust.m[2][0] = (right + left) / deltaX;
frust.m[2][1] = (top + bottom) / deltaY;
frust.m[2][2] = -(nearZ + farZ) / deltaZ;
frust.m[2][3] = -1.0f;
frust.m[3][2] = -2.0f * nearZ * farZ / deltaZ;
frust.m[3][0] = frust.m[3][1] = frust.m[3][3] = 0.0f;
ksMatrixMultiply(result, &frust, result);
}
void ksPerspective(KSMatrix4 *result, float fovy, float aspect, float nearZ, float farZ)
{
GLfloat frustumW, frustumH;
frustumH = tanf( fovy / 360.0f * M_PI ) * nearZ;
frustumW = frustumH * aspect;
ksFrustum( result, -frustumW, frustumW, -frustumH, frustumH, nearZ, farZ );
}
void ksOrtho(KSMatrix4 *result, float left, float right, float bottom, float top, float nearZ, float farZ)
{
float deltaX = right - left;
float deltaY = top - bottom;
float deltaZ = farZ - nearZ;
KSMatrix4 ortho;
if ( (deltaX == 0.0f) || (deltaY == 0.0f) || (deltaZ == 0.0f) )
return;
ksMatrixLoadIdentity(&ortho);
ortho.m[0][0] = 2.0f / deltaX;
ortho.m[3][0] = -(right + left) / deltaX;
ortho.m[1][1] = 2.0f / deltaY;
ortho.m[3][1] = -(top + bottom) / deltaY;
ortho.m[2][2] = -2.0f / deltaZ;
ortho.m[3][2] = -(nearZ + farZ) / deltaZ;
ksMatrixMultiply(result, &ortho, result);
}
9. GLESMath.h前一支檔案的定義檔,有矩陣及向量的結構定義。
#ifndef __GLESMATH_H__#define __GLESMATH_H__
#import <OpenGLES/ES2/gl.h>
#include <math.h>
#ifndef M_PI
#define M_PI 3.1415926535897932384626433832795f
#endif
#define DEG2RAD( a ) (((a) * M_PI) / 180.0f)
#define RAD2DEG( a ) (((a) * 180.f) / M_PI)
// angle indexes
#define PITCH 0 // up / down
#define YAW 1 // left / right
#define ROLL 2 // fall over
typedef unsigned char byte;
typedef struct
{
GLfloat m[3][3];
} KSMatrix3;
typedef struct
{
GLfloat m[4][4];
} KSMatrix4;
typedef struct KSVec3 {
GLfloat x;
GLfloat y;
GLfloat z;
} KSVec3;
typedef struct KSVec4 {
GLfloat x;
GLfloat y;
GLfloat z;
GLfloat w;
} KSVec4;
typedef struct {
GLfloat r;
GLfloat g;
GLfloat b;
GLfloat a;
} KSColor;
#ifdef __cplusplus
extern "C" {
#endif
unsigned int ksNextPot(unsigned int n);
void ksCopyMatrix4(KSMatrix4 * target, const KSMatrix4 * src);
void ksMatrix4ToMatrix3(KSMatrix3 * target, const KSMatrix4 * src);
//
/// multiply matrix specified by result with a scaling matrix and return new matrix in result
/// result Specifies the input matrix. Scaled matrix is returned in result.
/// sx, sy, sz Scale factors along the x, y and z axes respectively
//
void ksScale(KSMatrix4 *result, GLfloat sx, GLfloat sy, GLfloat sz);
//
/// multiply matrix specified by result with a translation matrix and return new matrix in result
/// result Specifies the input matrix. Translated matrix is returned in result.
/// tx, ty, tz Scale factors along the x, y and z axes respectively
//
void ksTranslate(KSMatrix4 *result, GLfloat tx, GLfloat ty, GLfloat tz);
//
/// multiply matrix specified by result with a rotation matrix and return new matrix in result
/// result Specifies the input matrix. Rotated matrix is returned in result.
/// angle Specifies the angle of rotation, in degrees.
/// x, y, z Specify the x, y and z coordinates of a vector, respectively
//
void ksRotate(KSMatrix4 *result, GLfloat angle, GLfloat x, GLfloat y, GLfloat z);
//
/// perform the following operation - result matrix = srcA matrix * srcB matrix
/// result Returns multiplied matrix
/// srcA, srcB Input matrices to be multiplied
//
void ksMatrixMultiply(KSMatrix4 *result, const KSMatrix4 *srcA, const KSMatrix4 *srcB);
//
//// return an identity matrix
//// result returns identity matrix
//
void ksMatrixLoadIdentity(KSMatrix4 *result);
//
/// multiply matrix specified by result with a perspective matrix and return new matrix in result
/// result Specifies the input matrix. new matrix is returned in result.
/// fovy Field of view y angle in degrees
/// aspect Aspect ratio of screen
/// nearZ Near plane distance
/// farZ Far plane distance
//
void ksPerspective(KSMatrix4 *result, float fovy, float aspect, float nearZ, float farZ);
//
/// multiply matrix specified by result with a perspective matrix and return new matrix in result
/// result Specifies the input matrix. new matrix is returned in result.
/// left, right Coordinates for the left and right vertical clipping planes
/// bottom, top Coordinates for the bottom and top horizontal clipping planes
/// nearZ, farZ Distances to the near and far depth clipping planes. These values are negative if plane is behind the viewer
//
void ksOrtho(KSMatrix4 *result, float left, float right, float bottom, float top, float nearZ, float farZ);
//
// multiply matrix specified by result with a perspective matrix and return new matrix in result
/// result Specifies the input matrix. new matrix is returned in result.
/// left, right Coordinates for the left and right vertical clipping planes
/// bottom, top Coordinates for the bottom and top horizontal clipping planes
/// nearZ, farZ Distances to the near and far depth clipping planes. Both distances must be positive.
//
void ksFrustum(KSMatrix4 *result, float left, float right, float bottom, float top, float nearZ, float farZ);
#ifdef __cplusplus
}
#endif
#endif // __GLESMATH_H__
10. GLESUtils.h,GLSL的載入程式定義區
#import <Foundation/Foundation.h>#include <OpenGLES/ES2/gl.h>
@interface GLESUtils : NSObject
// Create a shader object, load the shader source string, and compile the shader.
//
+(GLuint)loadShader:(GLenum)type withString:(NSString *)shaderString;
+(GLuint)loadShader:(GLenum)type withFilepath:(NSString *)shaderFilepath;
//
///
/// Load a vertex and fragment shader, create a program object, link program.
/// Errors output to log.
/// vertexShaderFilepath Vertex shader source file path.
/// fragmentShaderFilepath Fragment shader source file path
/// return A new program object linked with the vertex/fragment shader pair, 0 on failure
//
+(GLuint)loadProgram:(NSString *)vertexShaderFilepath withFragmentShaderFilepath:(NSString *)fragmentShaderFilepath;
@end
11. GLESUtils.m 載入GLSL的 主程式區
#import "GLESUtils.h"@implementation GLESUtils
+(GLuint)loadShader:(GLenum)type withFilepath:(NSString *)shaderFilepath
{
NSError* error;
NSString* shaderString = [NSString stringWithContentsOfFile:shaderFilepath
encoding:NSUTF8StringEncoding
error:&error];
if (!shaderString) {
NSLog(@"Error: loading shader file: %@ %@", shaderFilepath, error.localizedDescription);
return 0;
}
return [self loadShader:type withString:shaderString];
}
+(GLuint)loadShader:(GLenum)type withString:(NSString *)shaderString
{
// Create the shader object
GLuint shader = glCreateShader(type);
if (shader == 0) {
NSLog(@"Error: failed to create shader.");
return 0;
}
// Load the shader source
const char * shaderStringUTF8 = [shaderString UTF8String];
glShaderSource(shader, 1, &shaderStringUTF8, NULL);
// Compile the shader
glCompileShader(shader);
// Check the compile status
GLint compiled = 0;
glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
if (!compiled) {
GLint infoLen = 0;
glGetShaderiv ( shader, GL_INFO_LOG_LENGTH, &infoLen );
if (infoLen > 1) {
char * infoLog = malloc(sizeof(char) * infoLen);
glGetShaderInfoLog (shader, infoLen, NULL, infoLog);
NSLog(@"Error compiling shader:\n%s\n", infoLog );
free(infoLog);
}
glDeleteShader(shader);
return 0;
}
return shader;
}
+(GLuint)loadProgram:(NSString *)vertexShaderFilepath withFragmentShaderFilepath:(NSString *)fragmentShaderFilepath
{
// Load the vertex/fragment shaders
GLuint vertexShader = [self loadShader:GL_VERTEX_SHADER
withFilepath:vertexShaderFilepath];
if (vertexShader == 0)
return 0;
GLuint fragmentShader = [self loadShader:GL_FRAGMENT_SHADER
withFilepath:fragmentShaderFilepath];
if (fragmentShader == 0) {
glDeleteShader(vertexShader);
return 0;
}
// Create the program object
GLuint programHandle = glCreateProgram();
if (programHandle == 0)
return 0;
glAttachShader(programHandle, vertexShader);
glAttachShader(programHandle, fragmentShader);
// Link the program
glLinkProgram(programHandle);
// Check the link status
GLint linked;
glGetProgramiv(programHandle, GL_LINK_STATUS, &linked);
if (!linked) {
GLint infoLen = 0;
glGetProgramiv(programHandle, GL_INFO_LOG_LENGTH, &infoLen);
if (infoLen > 1){
char * infoLog = malloc(sizeof(char) * infoLen);
glGetProgramInfoLog(programHandle, infoLen, NULL, infoLog);
NSLog(@"Error linking program:\n%s\n", infoLog);
free(infoLog);
}
glDeleteProgram(programHandle );
return 0;
}
// Free up no longer needed shader resources
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
return programHandle;
}
@end
12. Shader檔案區,第一支檔案FragmentShader.glsl
precision mediump float;varying vec4 vDestinationColor;
void main()
{
gl_FragColor = vDestinationColor;
}
13. VertexShader.glsl
uniform mat4 projection;uniform mat4 modelView;
attribute vec4 vPosition;
attribute vec4 vSourceColor;
varying vec4 vDestinationColor;
void main(void)
{
gl_Position = projection * modelView * vPosition;
vDestinationColor = vSourceColor;
}
14. storyboard的設定,照原來的再加上一個自己定義修改的。
15. mainViewController.h 加上OPENGL繪圖區的變數定義
#import <UIKit/UIKit.h>#import "OpenGLView.h"
@interface mainViewController : UIViewController
{
OpenGLView * _openGLView;
}
@property (strong, nonatomic) IBOutlet OpenGLView *openGLView;
@end
16. mainViewController.m 主程式區
#import "mainViewController.h"
@interface mainViewController ()
@end
@implementation mainViewController
@synthesize openGLView = _openGLView;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)segChagne:(id)sender {
UISegmentedControl * segment = (UISegmentedControl *)sender;
int index = [segment selectedSegmentIndex];
[self.openGLView setCurrentSurface:index];
}
@end
17. OpenGLView.h OPENGL繪圖內部變數及相關運算函數定義區
#import <UIKit/UIKit.h>#import <QuartzCore/QuartzCore.h>
#include <OpenGLES/ES2/gl.h>
#include <OpenGLES/ES2/glext.h>
#import "GLESMath.h"
@interface DrawableVBO : NSObject
@property (nonatomic, assign) GLuint vertexBuffer;
@property (nonatomic, assign) GLuint lineIndexBuffer;
@property (nonatomic, assign) GLuint triangleIndexBuffer;
@property (nonatomic, assign) int vertexSize;
@property (nonatomic, assign) int lineIndexCount;
@property (nonatomic, assign) int triangleIndexCount;
- (void) cleanup;
@end
@interface OpenGLView : UIView
{
CAEAGLLayer* _eaglLayer;
EAGLContext* _context;
GLuint _colorRenderBuffer;
GLuint _frameBuffer;
GLuint _programHandle;
GLuint _positionSlot;
GLuint _modelViewSlot;
GLuint _projectionSlot;
GLuint _colorSlot;
KSMatrix4 _modelViewMatrix;
KSMatrix4 _projectionMatrix;
}
- (void)render;
- (void)cleanup;
- (void)setCurrentSurface:(int)index;
@end
18. OpenGLView.mm,此處使用C++語法,主要是要呼叫矩陣及向量運算所使用的C++原始碼。手指滑動來轉動Model的控制也寫在此檔上。
#import "OpenGLView.h"#import "GLESUtils.h"
#import "ParametricEquations.h"
#import "Quaternion.h"
//
// DrawableVBO implementation
//
@implementation DrawableVBO
@synthesize vertexBuffer, lineIndexBuffer, triangleIndexBuffer;
@synthesize vertexSize, lineIndexCount, triangleIndexCount;
- (void) cleanup
{
if (vertexBuffer != 0) {
glDeleteBuffers(1, &vertexBuffer);
vertexBuffer = 0;
}
if (lineIndexBuffer != 0) {
glDeleteBuffers(1, &lineIndexBuffer);
lineIndexBuffer = 0;
}
if (triangleIndexBuffer) {
glDeleteBuffers(1, &triangleIndexBuffer);
triangleIndexBuffer = 0;
}
}
@end
//
// OpenGLView anonymous category
//
@interface OpenGLView()
{
NSMutableArray * _vboArray;
DrawableVBO * _currentVBO;
ivec2 _fingerStart;
Quaternion _orientation;
Quaternion _previousOrientation;
KSMatrix4 _rotationMatrix;
}
- (void)setupLayer;
- (void)setupContext;
- (void)setupBuffers;
- (void)destoryBuffers;
- (void)setupProgram;
- (void)setupProjection;
- (DrawableVBO *)createVBO:(int)surfaceType;
- (void)setupVBOs;
- (void)destoryVBOs;
- (ISurface *)createSurface:(int)surfaceType;
- (vec3) mapToSphere:(ivec2) touchpoint;
- (void)updateSurfaceTransform;
- (void)resetRotation;
- (void)drawSurface;
@end
@implementation OpenGLView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
*/
#pragma mark - Initilize GL
+ (Class)layerClass {
// Support for OpenGL ES
return [CAEAGLLayer class];
}
- (void)setupLayer
{
//_eaglLayer = (CAEAGLLayer*) self.layer;
_eaglLayer = (CAEAGLLayer*) self.layer;
// Make CALayer visibale
_eaglLayer.opaque = YES;
// Set drawable properties
_eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];
}
- (void)setupContext
{
// Set OpenGL version, here is OpenGL ES 2.0
EAGLRenderingAPI api = kEAGLRenderingAPIOpenGLES2;
_context = [[EAGLContext alloc] initWithAPI:api];
if (!_context) {
NSLog(@" >> Error: Failed to initialize OpenGLES 2.0 context");
exit(1);
}
// Set OpenGL context
if (![EAGLContext setCurrentContext:_context]) {
_context = nil;
NSLog(@" >> Error: Failed to set current OpenGL context");
exit(1);
}
}
- (void)setupBuffers
{
glGenRenderbuffers(1, &_colorRenderBuffer);
// Set as current renderbuffer
glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderBuffer);
// Allocate color renderbuffer
[_context renderbufferStorage:GL_RENDERBUFFER fromDrawable:_eaglLayer];
glGenFramebuffers(1, &_frameBuffer);
// Set as current framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffer);
// Attach _colorRenderBuffer to _frameBuffer
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER, _colorRenderBuffer);
}
- (void)destoryBuffers
{
if (_colorRenderBuffer != 0) {
glDeleteRenderbuffers(1, &_colorRenderBuffer);
_colorRenderBuffer = 0;
}
if (_frameBuffer != 0) {
glDeleteFramebuffers(1, &_frameBuffer);
_frameBuffer = 0;
}
}
- (void)cleanup
{
[self destoryVBOs];
[self destoryBuffers];
if (_programHandle != 0) {
glDeleteProgram(_programHandle);
_programHandle = 0;
}
if (_context && [EAGLContext currentContext] == _context)
[EAGLContext setCurrentContext:nil];
_context = nil;
}
- (void)setupProgram
{
// Load shaders
//
NSString * vertexShaderPath = [[NSBundle mainBundle] pathForResource:@"VertexShader"
ofType:@"glsl"];
NSString * fragmentShaderPath = [[NSBundle mainBundle] pathForResource:@"FragmentShader"
ofType:@"glsl"];
_programHandle = [GLESUtils loadProgram:vertexShaderPath
withFragmentShaderFilepath:fragmentShaderPath];
if (_programHandle == 0) {
NSLog(@" >> Error: Failed to setup program.");
return;
}
glUseProgram(_programHandle);
// Get the attribute position slot from program
//
_positionSlot = glGetAttribLocation(_programHandle, "vPosition");
// Get the attribute color slot from program
//
_colorSlot = glGetAttribLocation(_programHandle, "vSourceColor");
// Get the uniform model-view matrix slot from program
//
_modelViewSlot = glGetUniformLocation(_programHandle, "modelView");
// Get the uniform projection matrix slot from program
//
_projectionSlot = glGetUniformLocation(_programHandle, "projection");
}
#pragma mark
-(void)setupProjection
{
// Generate a perspective matrix with a 60 degree FOV
//
float aspect = self.frame.size.width / self.frame.size.height;
ksMatrixLoadIdentity(&_projectionMatrix);
ksPerspective(&_projectionMatrix, 60.0, aspect, 4.0f, 12.0f);
// Load projection matrix
glUniformMatrix4fv(_projectionSlot, 1, GL_FALSE, (GLfloat*)&_projectionMatrix.m[0][0]);
glEnable(GL_CULL_FACE);
}
const int SurfaceSphere = 0;
const int SurfaceCone = 1;
const int SurfaceTorus = 2;
const int SurfaceTrefoilKnot = 3;
const int SurfaceKleinBottle = 4;
const int SurfaceMobiusStrip = 5;
const int SurfaceKirTest = 6;
const int SurfaceKirDifine = 7;
const int SurfaceMaxCount = 8;
- (ISurface *)createSurface:(int)type
{
ISurface * surface = NULL;
if (type == SurfaceCone) {
surface = new Cone(4, 1);
}
else if (type == SurfaceTorus) {
surface = new Torus(2.0f, 0.3f);
}
else if (type == SurfaceTrefoilKnot) {
surface = new TrefoilKnot(2.4f);
}
else if (type == SurfaceKleinBottle) {
surface = new KleinBottle(0.25f);
}
else if (type == SurfaceMobiusStrip) {
surface = new MobiusStrip(1.4);
}
else if (type == SurfaceKirTest) {
surface = new KirTest(0.3f);
}
else {
surface = new Sphere(2.0f);
}
return surface;
}
- (void)setCurrentSurface:(int)index
{
index = index % [_vboArray count];
_currentVBO = [_vboArray objectAtIndex:index];
[self resetRotation];
[self render];
}
- (DrawableVBO *)createVBO:(int)surfaceType
{
ISurface * surface = [self createSurface:surfaceType];
// Get vertice from surface.
//
int vertexSize = surface->GetVertexSize();
int vBufSize = surface->GetVertexCount() * vertexSize;
GLfloat * vbuf = new GLfloat[vBufSize];
surface->GenerateVertices(vbuf);
// Get triangle indice from surface
//
int triangleIndexCount = surface->GetTriangleIndexCount();
unsigned short * triangleBuf = new unsigned short[triangleIndexCount];
surface->GenerateTriangleIndices(triangleBuf);
// Get line indice from surface
//
int lineIndexCount = surface->GetLineIndexCount();
unsigned short * lineBuf = new unsigned short[lineIndexCount];
surface->GenerateLineIndices(lineBuf);
// Create the VBO for the vertice.
//
GLuint vertexBuffer;
glGenBuffers(1, &vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, vBufSize * sizeof(GLfloat), vbuf, GL_STATIC_DRAW);
// Create the VBO for the line indice
//
GLuint lineIndexBuffer;
glGenBuffers(1, &lineIndexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, lineIndexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, lineIndexCount * sizeof(GLushort), lineBuf, GL_STATIC_DRAW);
// Create the VBO for the triangle indice
//
GLuint triangleIndexBuffer;
glGenBuffers(1, &triangleIndexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, triangleIndexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, triangleIndexCount * sizeof(GLushort), triangleBuf, GL_STATIC_DRAW);
delete [] vbuf;
delete [] lineBuf;
delete [] triangleBuf;
delete surface;
DrawableVBO * vbo = [[DrawableVBO alloc] init];
vbo.vertexBuffer = vertexBuffer;
vbo.lineIndexBuffer = lineIndexBuffer;
vbo.triangleIndexBuffer = triangleIndexBuffer;
vbo.vertexSize = vertexSize;
vbo.lineIndexCount = lineIndexCount;
vbo.triangleIndexCount = triangleIndexCount;
return vbo;
}
- (void)setupVBOs
{
for (int i = 0; i < SurfaceMaxCount; i++) {
DrawableVBO * vbo = [self createVBO:i];
[_vboArray addObject:vbo];
vbo = nil;
}
[self setCurrentSurface:0];
}
- (void)destoryVBOs
{
for (DrawableVBO * vbo in _vboArray) {
[vbo cleanup];
}
_vboArray = nil;
_currentVBO = nil;
}
- (void)resetRotation
{
ksMatrixLoadIdentity(&_rotationMatrix);
_previousOrientation.ToIdentity();
_orientation.ToIdentity();
}
- (void)updateSurfaceTransform
{
ksMatrixLoadIdentity(&_modelViewMatrix);
ksTranslate(&_modelViewMatrix, 0.0, 0.0, -7);
ksMatrixMultiply(&_modelViewMatrix, &_rotationMatrix, &_modelViewMatrix);
// Load the model-view matrix
glUniformMatrix4fv(_modelViewSlot, 1, GL_FALSE, (GLfloat*)&_modelViewMatrix.m[0][0]);
}
- (void)drawSurface
{
if (_currentVBO == nil)
return;
glBindBuffer(GL_ARRAY_BUFFER, [_currentVBO vertexBuffer]);
glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE, [_currentVBO vertexSize] * sizeof(GLfloat), 0);
glEnableVertexAttribArray(_positionSlot);
// Draw the red triangles.
//
glVertexAttrib4f(_colorSlot, 1.0, 0.0, 0.0, 1.0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, [_currentVBO triangleIndexBuffer]);
glDrawElements(GL_TRIANGLES, [_currentVBO triangleIndexCount], GL_UNSIGNED_SHORT, 0);
// Draw the black lines.
//
glVertexAttrib4f(_colorSlot, 0.0, 0.0, 0.0, 1.0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, [_currentVBO lineIndexBuffer]);
glDrawElements(GL_LINES, [_currentVBO lineIndexCount], GL_UNSIGNED_SHORT, 0);
glDisableVertexAttribArray(_positionSlot);
}
- (void)render
{
if (_context == nil)
return;
glClearColor(0.0, 1.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
// Setup viewport
//
glViewport(0, 0, self.frame.size.width, self.frame.size.height);
[self updateSurfaceTransform];
[self drawSurface];
[_context presentRenderbuffer:GL_RENDERBUFFER];
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
[self setupLayer];
[self setupContext];
[self setupProgram];
[self setupProjection];
[self resetRotation];
_vboArray = [[NSMutableArray alloc] init];
}
return self;
}
- (void)layoutSubviews
{
[EAGLContext setCurrentContext:_context];
glUseProgram(_programHandle);
[self destoryBuffers];
[self setupBuffers];
[self setupVBOs];
[self render];
}
#pragma mark
#pragma mark - Touch events
- (void) touchesBegan: (NSSet*) touches withEvent: (UIEvent*) event
{
UITouch* touch = [touches anyObject];
CGPoint location = [touch locationInView: self];
_fingerStart = ivec2(location.x, location.y);
_previousOrientation = _orientation;
}
- (void) touchesEnded: (NSSet*) touches withEvent: (UIEvent*) event
{
UITouch* touch = [touches anyObject];
CGPoint location = [touch locationInView: self];
ivec2 touchPoint = ivec2(location.x, location.y);
vec3 start = [self mapToSphere:_fingerStart];
vec3 end = [self mapToSphere:touchPoint];
Quaternion delta = Quaternion::CreateFromVectors(start, end);
_orientation = delta.Rotated(_previousOrientation);
_orientation.ToMatrix4(&_rotationMatrix);
[self render];
}
- (void) touchesMoved: (NSSet*) touches withEvent: (UIEvent*) event
{
UITouch* touch = [touches anyObject];
CGPoint location = [touch locationInView: self];
ivec2 touchPoint = ivec2(location.x, location.y);
vec3 start = [self mapToSphere:_fingerStart];
vec3 end = [self mapToSphere:touchPoint];
Quaternion delta = Quaternion::CreateFromVectors(start, end);
_orientation = delta.Rotated(_previousOrientation);
_orientation.ToMatrix4(&_rotationMatrix);
[self render];
}
- (vec3) mapToSphere:(ivec2) touchpoint
{
ivec2 centerPoint = ivec2(self.frame.size.width/2, self.frame.size.height/2);
float radius = self.frame.size.width/3;
float safeRadius = radius - 1;
vec2 p = touchpoint - centerPoint;
// Flip the Y axis because pixel coords increase towards the bottom.
p.y = -p.y;
if (p.Length() > safeRadius) {
float theta = atan2(p.y, p.x);
p.x = safeRadius * cos(theta);
p.y = safeRadius * sin(theta);
}
float z = sqrt(radius * radius - p.LengthSquared());
vec3 mapped = vec3(p.x, p.y, z);
return mapped / radius;
}
#pragma mark
@end
19. 結果







