將iphone 3D上SimpleWireframe這個例子轉移到Ipad上,以瞭解細節設計,並在程式上加上個別注解。此例是實作五的另一個版本,複雜度稍高一點。
1. 首先開一個新的專案
2.加入所需檔案
3. 先從Interfaces.hpp開始
#pragma once
#include "Vector.hpp"
#include "Quaternion.hpp"
#include <vector>
#include <string>
using std::vector;
using std::string;
enum VertexFlags {
VertexFlagsNormals = 1 << 0, // ==1
VertexFlagsTexCoords = 1 << 1, // ==2
};
struct IApplicationEngine {
virtual void Initialize(int width, int height) = 0;
virtual void Render() const = 0;
virtual void UpdateAnimation(float timeStep) = 0;
virtual void OnFingerUp(ivec2 location) = 0;
virtual void OnFingerDown(ivec2 location) = 0;
virtual void OnFingerMove(ivec2 oldLocation, ivec2 newLocation) = 0;
virtual ~IApplicationEngine() {}
};
struct ISurface {
virtual int GetVertexCount() const = 0;
virtual int GetLineIndexCount() const = 0;
virtual int GetTriangleIndexCount() const = 0;
virtual void GenerateVertices(vector<float>& vertices,
unsigned char flags = 0) const = 0;
virtual void GenerateLineIndices(vector<unsigned short>& indices) const = 0;
virtual void GenerateTriangleIndices(vector<unsigned short>& indices) const = 0;
virtual ~ISurface() {}
};
//Describes the dynamic visual properties of a surface; gets passed from the application engine to the rendering engine at every frame.
struct Visual {
vec3 Color;
ivec2 LowerLeft;
ivec2 ViewportSize;
Quaternion Orientation;
};
struct IRenderingEngine {
virtual void Initialize(const vector<ISurface*>& surfaces) = 0;
virtual void Render(const vector<Visual>& visuals) const = 0;
virtual ~IRenderingEngine() {}
};
// 此處的CreateApplicationEngine有重複到Function name,因此使用namespace
namespace ParametricViewer { IApplicationEngine* CreateApplicationEngine(IRenderingEngine*); }
//namespace ObjViewer { IApplicationEngine* CreateApplicationEngine(IRenderingEngine*, IResourceManager*); }
//namespace Darwin { IResourceManager* CreateResourceManager(); }
namespace WireframeES1 { IRenderingEngine* CreateRenderingEngine(); }
namespace WireframeES2 { IRenderingEngine* CreateRenderingEngine(); }
4. ParametricEquations.hpp 六個3D Model的 Equation
#include "ParametricSurface.hpp"
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;
};
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;
};
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;
};
5. ParametricSurface.hpp/cpp 計算 Vertex及 Indices
ParametricSurface.hpp
struct ParametricInterval {
ivec2 Divisions;
vec2 UpperBound;
vec2 TextureCount;
};
class ParametricSurface : public ISurface {
public:
int GetVertexCount() const;
int GetLineIndexCount() const;
int GetTriangleIndexCount() const;
void GenerateVertices(vector<float>& vertices, unsigned char flags) const;
void GenerateLineIndices(vector<unsigned short>& indices) const;
void GenerateTriangleIndices(vector<unsigned short>& indices) const;
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;
};
ParametricSurface.cpp
#include "ParametricSurface.hpp"
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);
}
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(vector<float>& vertices,
unsigned char flags) const
{
// 其中 VertexFlagsNormals == 1 , VertexFlagsTexCoords == 2 , flags == 0
int floatsPerVertex = 3;
if (flags & VertexFlagsNormals)
floatsPerVertex += 3;
if (flags & VertexFlagsTexCoords)
floatsPerVertex += 2;
vertices.resize(GetVertexCount() * floatsPerVertex);
// 重新制訂記憶區塊大小
float* attribute = &vertices[0];
// attribute 指向Vertices的第0個位置
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);
// 內部的this 就是 range 本身, 將attribute寫到 range所指的位置,然後range的位置指向下一個 Vertices
// 書本解釋 Copy the vec3 position into the flat floating-point buffer. The Write method returns an updated pointer.
//printf("VertexFlagsNormals = %d", VertexFlagsNormals);
// 本例不做 Normal 及 Texture
// Compute Normal
if (flags & VertexFlagsNormals) { // VertexFlagsNormals ==1
float s = i, t = j;
// 本例此處永不進入 flags == 0
// 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);
}
// Compute Texture Coordinates
if (flags & VertexFlagsTexCoords) {
float s = m_textureCount.x * i / m_slices.x;
// 本例此處永不進入 flags == 0
float t = m_textureCount.y * j / m_slices.y;
attribute = vec2(s, t).Write(attribute);
}
}
}
}
void ParametricSurface::GenerateLineIndices(vector<unsigned short>& indices) const
{
indices.resize(GetLineIndexCount());
vector<unsigned short>::iterator index = indices.begin();
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(vector<unsigned short>& indices) const
{
indices.resize(GetTriangleIndexCount());
vector<unsigned short>::iterator index = indices.begin();
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;
}
}
6. ApplicationEngine.ParametricViewer.cpp此處應用了右圖的做法,加入一個Application接面,作為軌跡球對應手指轉動的計算。
#include "Interfaces.hpp"
#include "ParametricEquations.hpp"
using namespace std;
namespace ParametricViewer {
// 有一些function命名可能會與其他重複,因此使用namespace
static const int SurfaceCount = 6;
static const int ButtonCount = SurfaceCount - 1;
struct Animation {
bool Active;
float Elapsed;
float Duration;
Visual StartingVisuals[SurfaceCount];
Visual EndingVisuals[SurfaceCount];
};
class ApplicationEngine : public IApplicationEngine {
public:
ApplicationEngine(IRenderingEngine* renderingEngine);
~ApplicationEngine();
void Initialize(int width, int height);
void OnFingerUp(ivec2 location);
void OnFingerDown(ivec2 location);
void OnFingerMove(ivec2 oldLocation, ivec2 newLocation);
void Render() const;
void UpdateAnimation(float dt);
private:
void PopulateVisuals(Visual* visuals) const;
int MapToButton(ivec2 touchpoint) const;
vec3 MapToSphere(ivec2 touchpoint) const;
float m_trackballRadius;
ivec2 m_screenSize;
ivec2 m_centerPoint;
ivec2 m_fingerStart;
bool m_spinning;
Quaternion m_orientation;
Quaternion m_previousOrientation;
int m_currentSurface;
ivec2 m_buttonSize;
int m_pressedButton;
int m_buttonSurfaces[ButtonCount];
Animation m_animation;
IRenderingEngine* m_renderingEngine;
};
IApplicationEngine* CreateApplicationEngine(IRenderingEngine* renderingEngine)
{
return new ApplicationEngine(renderingEngine);
}
ApplicationEngine::ApplicationEngine(IRenderingEngine* renderingEngine) :
m_spinning(false),
m_pressedButton(-1),
m_renderingEngine(renderingEngine)
{
m_animation.Active = false;
m_buttonSurfaces[0] = 0;
m_buttonSurfaces[1] = 1;
m_buttonSurfaces[2] = 4;
m_buttonSurfaces[3] = 3;
m_buttonSurfaces[4] = 2;
m_currentSurface = 5;
}
ApplicationEngine::~ApplicationEngine()
{
delete m_renderingEngine;
}
void ApplicationEngine::Initialize(int width, int height)
{
m_trackballRadius = width / 3;
m_buttonSize.y = height / 10;
m_buttonSize.x = 4 * m_buttonSize.y / 3;
m_screenSize = ivec2(width, height - m_buttonSize.y);
m_centerPoint = m_screenSize / 2;
vector<ISurface*> surfaces(SurfaceCount);
surfaces[0] = new Cone(3, 1);
// 設定半徑及高,其他
surfaces[1] = new Sphere(1.4f);
surfaces[2] = new Torus(1.4f, 0.3f);
surfaces[3] = new TrefoilKnot(1.8f);
surfaces[4] = new KleinBottle(0.2f);
surfaces[5] = new MobiusStrip(1);
m_renderingEngine->Initialize(surfaces);
for (int i = 0; i < SurfaceCount; i++)
delete surfaces[i];
}
void ApplicationEngine::PopulateVisuals(Visual* visuals) const
{
//設定所有圖像的顏色與大小,包含主圖及Button
for (int buttonIndex = 0; buttonIndex < ButtonCount; buttonIndex++) {
int visualIndex = m_buttonSurfaces[buttonIndex];
visuals[visualIndex].Color = vec3(0.25f*3, 0.25f, 0.25f);
// Button上的顏色
if (m_pressedButton == buttonIndex)
visuals[visualIndex].Color = vec3(0.5f, 0.5f*3, 0.5f);
// 點選後的Button顏色
// 設定每一個Button上的圖案大小
visuals[visualIndex].ViewportSize = m_buttonSize;
visuals[visualIndex].LowerLeft.x = buttonIndex * m_buttonSize.x;
visuals[visualIndex].LowerLeft.y = 0;
visuals[visualIndex].Orientation = Quaternion();
// 基本button的旋轉值為0
}
// 顯示主圖的顏色,m_spinning代表手指按下的狀態,蓋掉前面所設的值
visuals[m_currentSurface].Color = m_spinning ? vec3(1, 0, 0.75f) : vec3(1*3, 1, 0.5f);
visuals[m_currentSurface].LowerLeft = ivec2(0, m_buttonSize.y);
visuals[m_currentSurface].ViewportSize = ivec2(m_screenSize.x, m_screenSize.y);
visuals[m_currentSurface].Orientation = m_orientation;
//主圖的旋轉值
}
void ApplicationEngine::Render() const
{
vector<Visual> visuals(SurfaceCount);
if (!m_animation.Active) {
PopulateVisuals(&visuals[0]);
} else {
float t = m_animation.Elapsed / m_animation.Duration;
for (int i = 0; i < SurfaceCount; i++) {
// 找出起始的visuals[x] 及最後的visuals[y]
const Visual& start = m_animation.StartingVisuals[i];
const Visual& end = m_animation.EndingVisuals[i];
Visual& tweened = visuals[i];
// 這時的 visuals[]中是空的
// 以下將所有的值重新填到新的visuals[]中,根據現在所見的狀態
tweened.Color = start.Color.Lerp(t, end.Color);
//將顏色做線性插補取得時間變化值
tweened.LowerLeft = start.LowerLeft.Lerp(t, end.LowerLeft);
tweened.ViewportSize = start.ViewportSize.Lerp(t, end.ViewportSize);
tweened.Orientation = start.Orientation.Slerp(t, end.Orientation);
}
}
m_renderingEngine->Render(visuals);
}
void ApplicationEngine::UpdateAnimation(float dt)
{
if (m_animation.Active) {
// 改選成另一個物件
m_animation.Elapsed += dt;
if (m_animation.Elapsed > m_animation.Duration)
m_animation.Active = false;
}
}
// 3. 手指離開
void ApplicationEngine::OnFingerUp(ivec2 location)
{
m_spinning = false;
if (m_pressedButton != -1 && m_pressedButton == MapToButton(location) &&
!m_animation.Active)
// 如果按選了其他的物件,就進行以下的程序
{
m_animation.Active = true;
m_animation.Elapsed = 0;
m_animation.Duration = 0.25f;
PopulateVisuals(&m_animation.StartingVisuals[0]);
swap(m_buttonSurfaces[m_pressedButton], m_currentSurface);
// 點選的Button圖像與主圖像交換
PopulateVisuals(&m_animation.EndingVisuals[0]);
}
m_pressedButton = -1;
}
// 1. 壓下手指
void ApplicationEngine::OnFingerDown(ivec2 location)
{
m_fingerStart = location;
m_previousOrientation = m_orientation;
// 取得現在的旋轉值
m_pressedButton = MapToButton(location);
if (m_pressedButton == -1)
m_spinning = true;
}
// 2. 移動手指讓物件轉動
void ApplicationEngine::OnFingerMove(ivec2 oldLocation, ivec2 location)
{
if (m_spinning) {
vec3 start = MapToSphere(m_fingerStart);
vec3 end = MapToSphere(location);
Quaternion delta = Quaternion::CreateFromVectors(start, end); /
/ 取得方向向量
m_orientation = delta.Rotated(m_previousOrientation);
// 根據前一個旋轉值,計算再次旋轉後的值,此為主圖所用
}
if (m_pressedButton != -1 && m_pressedButton != MapToButton(location))
m_pressedButton = -1;
}
// 確認所按的位置為主圖像的位置,並回傳所按的點,經計算後的3D位置。
vec3 ApplicationEngine::MapToSphere(ivec2 touchpoint) const
{
vec2 p = touchpoint - m_centerPoint;
// Flip the Y axis because pixel coords increase towards the bottom.
p.y = -p.y;
const float radius = m_trackballRadius;
const float safeRadius = radius - 1;
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;
}
// 確認按到了Button 的位置,並回傳所按的Button代號。
int ApplicationEngine::MapToButton(ivec2 touchpoint) const
{
if (touchpoint.y < m_screenSize.y - m_buttonSize.y)
return -1;
int buttonIndex = touchpoint.x / m_buttonSize.x;
if (buttonIndex >= ButtonCount)
return -1;
return buttonIndex;
}
}
7. RenderingEngine.WireframeES1.cpp
#include <OpenGLES/ES1/gl.h>
#include <OpenGLES/ES1/glext.h>
#include "Interfaces.hpp"
#include "Matrix.hpp"
namespace WireframeES1 {
struct Drawable {
GLuint VertexBuffer;
GLuint IndexBuffer;
int IndexCount;
};
class RenderingEngine : public IRenderingEngine {
public:
RenderingEngine();
void Initialize(const vector<ISurface*>& surfaces);
void Render(const vector<Visual>& visuals) const;
private:
vector<Drawable> m_drawables;
GLuint m_colorRenderbuffer;
mat4 m_translation;
};
IRenderingEngine* CreateRenderingEngine()
{
return new RenderingEngine();
}
RenderingEngine::RenderingEngine()
{
glGenRenderbuffersOES(1, &m_colorRenderbuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, m_colorRenderbuffer);
}
void RenderingEngine::Initialize(const vector<ISurface*>& surfaces)
{
vector<ISurface*>::const_iterator surface;
for (surface = surfaces.begin(); surface != surfaces.end(); ++surface) {
// Create the VBO for the vertices.
vector<float> vertices;
(*surface)->GenerateVertices(vertices);
GLuint vertexBuffer;
glGenBuffers(1, &vertexBuffer);
//設定GPU memory 給 Vertex用
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glBufferData(GL_ARRAY_BUFFER,
// 將vertex資料存到GPU memory
vertices.size() * sizeof(vertices[0]),
// 確認記憶體大小
&vertices[0],
GL_STATIC_DRAW);
// Create a new VBO for the indices if needed.
int indexCount = (*surface)->GetLineIndexCount();
GLuint indexBuffer;
if (!m_drawables.empty() && indexCount == m_drawables[0].IndexCount) {
indexBuffer = m_drawables[0].IndexBuffer;
} else {
vector<GLushort> indices(indexCount);
(*surface)->GenerateLineIndices(indices);
// 4個 indices為一組
glGenBuffers(1, &indexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
indexCount * sizeof(GLushort),
&indices[0],
GL_STATIC_DRAW);
// 表示该缓存区不会被修改
}
Drawable drawable = { vertexBuffer, indexBuffer, indexCount};
m_drawables.push_back(drawable);
}
// Create the framebuffer object.
GLuint framebuffer;
glGenFramebuffersOES(1, &framebuffer);
glBindFramebufferOES(GL_FRAMEBUFFER_OES, framebuffer);
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES,
GL_RENDERBUFFER_OES, m_colorRenderbuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, m_colorRenderbuffer);
glEnableClientState(GL_VERTEX_ARRAY);
m_translation = mat4::Translate(0, 0, -7);
}
void RenderingEngine::Render(const vector<Visual>& visuals) const
{
glClearColor(0.5f, 0.5f, 0.5f, 1);
glClear(GL_COLOR_BUFFER_BIT);
vector<Visual>::const_iterator visual = visuals.begin();
for (int visualIndex = 0; visual != visuals.end(); ++visual, ++visualIndex) {
// Set the viewport transform.
ivec2 size = visual->ViewportSize;
ivec2 lowerLeft = visual->LowerLeft;
glViewport(lowerLeft.x, lowerLeft.y, size.x, size.y);
// Set the model-view transform.
mat4 rotation = visual->Orientation.ToMatrix();
mat4 modelview = rotation * m_translation;
glMatrixMode(GL_MODELVIEW);
glLoadMatrixf(modelview.Pointer());
// Set the projection transform.
float h = 4.0f * size.y / size.x;
mat4 projection = mat4::Frustum(-2, 2, -h / 2, h / 2, 5, 10);
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(projection.Pointer());
// Set the color.
vec3 color = visual->Color;
glColor4f(color.x, color.y, color.z, 1);
// Draw the wireframe.
int stride = sizeof(vec3);
const Drawable& drawable = m_drawables[visualIndex];
glBindBuffer(GL_ARRAY_BUFFER, drawable.VertexBuffer);
glVertexPointer(3, GL_FLOAT, stride, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, drawable.IndexBuffer);
glDrawElements(GL_LINES, drawable.IndexCount, GL_UNSIGNED_SHORT, 0);
}
}
}
8. RenderingEngine.WireframeES2.cpp
#include <OpenGLES/ES2/gl.h>
#include <OpenGLES/ES2/glext.h>
#include "Interfaces.hpp"
#include "Matrix.hpp"
#include <iostream>
namespace WireframeES2 {
#define STRINGIFY(A) #A
#include "./Shaders/Simple.es2.vert"
#include "./Shaders/Simple.es2.frag"
struct Drawable {
GLuint VertexBuffer;
GLuint IndexBuffer;
int IndexCount;
};
class RenderingEngine : public IRenderingEngine {
public:
RenderingEngine();
void Initialize(const vector<ISurface*>& surfaces);
void Render(const vector<Visual>& visuals) const;
private:
GLuint BuildShader(const char* source, GLenum shaderType) const;
GLuint BuildProgram(const char* vShader, const char* fShader) const;
vector<Drawable> m_drawables;
GLuint m_colorRenderbuffer;
GLint m_projectionUniform;
GLint m_modelviewUniform;
GLuint m_positionSlot;
GLuint m_colorSlot;
mat4 m_translation;
};
IRenderingEngine* CreateRenderingEngine()
{
return new RenderingEngine();
}
RenderingEngine::RenderingEngine()
{
glGenRenderbuffers(1, &m_colorRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, m_colorRenderbuffer);
}
void RenderingEngine::Initialize(const vector<ISurface*>& surfaces)
{
vector<ISurface*>::const_iterator surface;
for (surface = surfaces.begin(); surface != surfaces.end(); ++surface) {
// Create the VBO for the vertices.
vector<float> vertices;
(*surface)->GenerateVertices(vertices);
GLuint vertexBuffer;
glGenBuffers(1, &vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glBufferData(GL_ARRAY_BUFFER,
vertices.size() * sizeof(vertices[0]),
&vertices[0],
GL_STATIC_DRAW);
// Create a new VBO for the indices if needed.
int indexCount = (*surface)->GetLineIndexCount();
GLuint indexBuffer;
if (!m_drawables.empty() && indexCount == m_drawables[0].IndexCount) {
indexBuffer = m_drawables[0].IndexBuffer;
} else {
vector<GLushort> indices(indexCount);
(*surface)->GenerateLineIndices(indices);
glGenBuffers(1, &indexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
indexCount * sizeof(GLushort),
&indices[0],
GL_STATIC_DRAW);
}
Drawable drawable = { vertexBuffer, indexBuffer, indexCount};
m_drawables.push_back(drawable);
// 新增drawable至 m_drawables 的尾端,必要時會進行記憶體配置。
}
// Create the framebuffer object.
GLuint framebuffer;
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
// 设置FrameBuffer并使用glFramebufferRenderBuffer相互关联
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, //
GL_RENDERBUFFER, m_colorRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, m_colorRenderbuffer);
// Create the GLSL program.
GLuint simpleProgram = BuildProgram(SimpleVertexShader, SimpleFragmentShader);
glUseProgram(simpleProgram);
m_positionSlot = glGetAttribLocation(simpleProgram, "Position");
m_colorSlot = glGetAttribLocation(simpleProgram, "SourceColor");
glEnableVertexAttribArray(m_positionSlot);
// Set up some matrices.
m_translation = mat4::Translate(0, 0, -7);
m_projectionUniform = glGetUniformLocation(simpleProgram, "Projection");
m_modelviewUniform = glGetUniformLocation(simpleProgram, "Modelview");
}
void RenderingEngine::Render(const vector<Visual>& visuals) const
{
glClearColor(0.5f, 0.5f, 0.5f, 1);
glClear(GL_COLOR_BUFFER_BIT);
vector<Visual>::const_iterator visual = visuals.begin();
// 將所有的圖像都畫出,Button上的圖像,使用相對小的Size(64,48), 主圖大小為(320,432)
// 0~5, 5指的是主畫面
for (int visualIndex = 0; visual != visuals.end(); ++visual, ++visualIndex) {
// Set the viewport transform.
ivec2 size = visual->ViewportSize;
ivec2 lowerLeft = visual->LowerLeft;
glViewport(lowerLeft.x, lowerLeft.y, size.x, size.y);
// Set the model-view transform.
mat4 rotation = visual->Orientation.ToMatrix();
// 只有主圖像的旋轉四元值被讀入,並轉為矩陣。
mat4 modelview = rotation * m_translation; // m_translation =[0,0, -7]
glUniformMatrix4fv(m_modelviewUniform, 1, 0, modelview.Pointer());
// Set the projection transform.
float h = 4.0f * size.y / size.x;
mat4 projectionMatrix = mat4::Frustum(-2, 2, -h / 2, h / 2, 5, 10);
glUniformMatrix4fv(m_projectionUniform, 1, 0, projectionMatrix.Pointer());
// Set the color.
vec3 color = visual->Color;
glVertexAttrib4f(m_colorSlot, color.x, color.y, color.z, 1);
// Draw the wireframe.
int stride = sizeof(vec3);
const Drawable& drawable = m_drawables[visualIndex];
glBindBuffer(GL_ARRAY_BUFFER, drawable.VertexBuffer);
glVertexAttribPointer(m_positionSlot, 3, GL_FLOAT, GL_FALSE, stride, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, drawable.IndexBuffer);
glDrawElements(GL_LINES, drawable.IndexCount, GL_UNSIGNED_SHORT, 0);
}
}
GLuint RenderingEngine::BuildShader(const char* source, GLenum shaderType) const
{
GLuint shaderHandle = glCreateShader(shaderType);
glShaderSource(shaderHandle, 1, &source, 0);
glCompileShader(shaderHandle);
GLint compileSuccess;
glGetShaderiv(shaderHandle, GL_COMPILE_STATUS, &compileSuccess);
if (compileSuccess == GL_FALSE) {
GLchar messages[256];
glGetShaderInfoLog(shaderHandle, sizeof(messages), 0, &messages[0]);
std::cout << messages;
exit(1);
}
return shaderHandle;
}
GLuint RenderingEngine::BuildProgram(const char* vertexShaderSource,
const char* fragmentShaderSource) const
{
GLuint vertexShader = BuildShader(vertexShaderSource, GL_VERTEX_SHADER);
GLuint fragmentShader = BuildShader(fragmentShaderSource, GL_FRAGMENT_SHADER);
GLuint programHandle = glCreateProgram();
glAttachShader(programHandle, vertexShader);
glAttachShader(programHandle, fragmentShader);
glLinkProgram(programHandle);
GLint linkSuccess;
glGetProgramiv(programHandle, GL_LINK_STATUS, &linkSuccess);
if (linkSuccess == GL_FALSE) {
GLchar messages[256];
glGetProgramInfoLog(programHandle, sizeof(messages), 0, &messages[0]);
std::cout << messages;
exit(1);
}
return programHandle;
}
}
9. GLView.h/.mm
GLView.h
#import <UIKit/UIKit.h>
#import "Interfaces.hpp"
#import <QuartzCore/QuartzCore.h>
@interface GLView : UIView
{
@private
IApplicationEngine* m_applicationEngine;
IRenderingEngine* m_renderingEngine;
EAGLContext* m_context;
float m_timestamp;
}
- (void) drawView: (CADisplayLink*) displayLink;
@end
GLView.mm
#import "GLView.h"
@implementation GLView
+ (Class) layerClass
{
return [CAEAGLLayer class];
}
- (id) initWithFrame: (CGRect) frame
{
if (self = [super initWithFrame:frame])
{
CAEAGLLayer* eaglLayer = (CAEAGLLayer*) self.layer;
eaglLayer.opaque = YES;
//EAGLRenderingAPI api = kEAGLRenderingAPIOpenGLES2;
EAGLRenderingAPI api = kEAGLRenderingAPIOpenGLES1;
m_context = [[EAGLContext alloc] initWithAPI:api];
if (!m_context) {
api = kEAGLRenderingAPIOpenGLES1;
m_context = [[EAGLContext alloc] initWithAPI:api];
}
if (!m_context || ![EAGLContext setCurrentContext:m_context]) {
//[self release];
return nil;
}
if (api == kEAGLRenderingAPIOpenGLES1) {
NSLog(@"Using OpenGL ES 1.1");
m_renderingEngine = WireframeES1::CreateRenderingEngine();
} else {
NSLog(@"Using OpenGL ES 2.0");
m_renderingEngine = WireframeES2::CreateRenderingEngine();
// 完成 m_colorRenderbuffer 設定
}
m_applicationEngine = ParametricViewer::CreateApplicationEngine(m_renderingEngine);
[m_context
renderbufferStorage:GL_RENDERBUFFER
fromDrawable: eaglLayer];
int width = CGRectGetWidth(frame);
int height = CGRectGetHeight(frame);
m_applicationEngine->Initialize(width, height);
[self drawView: nil];
m_timestamp = CACurrentMediaTime();
CADisplayLink* displayLink;
displayLink = [CADisplayLink displayLinkWithTarget:self
selector:@selector(drawView:)];
[displayLink addToRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
}
return self;
}
- (void) drawView: (CADisplayLink*) displayLink
{
if (displayLink != nil) {
float elapsedSeconds = displayLink.timestamp - m_timestamp;
m_timestamp = displayLink.timestamp;
m_applicationEngine->UpdateAnimation(elapsedSeconds);
}
m_applicationEngine->Render();
[m_context presentRenderbuffer:GL_RENDERBUFFER];
}
- (void) touchesBegan: (NSSet*) touches withEvent: (UIEvent*) event
{
UITouch* touch = [touches anyObject];
CGPoint location = [touch locationInView: self];
m_applicationEngine->OnFingerDown(ivec2(location.x, location.y));
}
- (void) touchesEnded: (NSSet*) touches withEvent: (UIEvent*) event
{
UITouch* touch = [touches anyObject];
CGPoint location = [touch locationInView: self];
m_applicationEngine->OnFingerUp(ivec2(location.x, location.y));
//傳入所按的平面位置
}
- (void) touchesMoved: (NSSet*) touches withEvent: (UIEvent*) event
{
UITouch* touch = [touches anyObject];
CGPoint previous = [touch previousLocationInView: self];
CGPoint current = [touch locationInView: self];
m_applicationEngine->OnFingerMove(ivec2(previous.x, previous.y),
ivec2(current.x, current.y));
}
@end
10. mainViewController.h/.mm
mainViewController.h
#import <UIKit/UIKit.h>
#include "GLView.h"
@interface mainViewController : UIViewController
{
UIWindow* m_window;
GLView* m_view;
}
@end
mainViewController.mm
#import "mainViewController.h"
@interface mainViewController ()
@end
@implementation mainViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
CGRect screenBounds = [[UIScreen mainScreen] bounds];
m_window = [[UIWindow alloc] initWithFrame: screenBounds];
m_view = [[GLView alloc] initWithFrame: screenBounds];
[m_window addSubview: m_view];
[m_window makeKeyAndVisible];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
11. 最後是UI控制 mainViewController.h/.mm
mainViewController.h
#import <UIKit/UIKit.h>
#include "GLView.h"
@interface mainViewController : UIViewController
{
UIWindow* m_window;
GLView* m_view;
}
@end
mainViewController.mm
#import "mainViewController.h"
@interface mainViewController ()
@end
@implementation mainViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
CGRect screenBounds = [[UIScreen mainScreen] bounds];
m_window = [[UIWindow alloc] initWithFrame: screenBounds];
m_view = [[GLView alloc] initWithFrame: screenBounds];
[m_window addSubview: m_view];
[m_window makeKeyAndVisible];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
12.結果,因為simulator直立的顯示太大,因此將其旋轉