2013年6月22日 星期六

OpenGL基本瞭解(十七)(Texture mapping)


材質貼圖,又稱紋理貼圖,在電腦圖形學中是把儲存在內存里的點陣圖包 裹到3D渲染物體的表面。紋理貼圖給物體提供了豐富的細節,用簡單的方式類比出了複雜的外觀。一個影像(紋理)被貼(對映)到場景中的一個簡單形體上,就 像印花貼到一個平面上一樣。這大大減少了在場景中製作形體和紋理的計算量。例如,可以建立一個球並把臉的紋理貼上去,這樣就不用處理鼻子和眼睛的形狀了。
隨著圖形卡功 能越來越強,理論上材質貼圖變得越來越不必要,而三維繪製(渲染)成了常用的工具。但事實上,最近的趨勢是使用更大和更多的紋理影像,再加上把多幅紋理組 合到同個物體的不同角度的複雜技術。(這在即時圖形學中更為重要,因為同時顯示的紋理個數是可用圖形內存容量的函式。)
最後顯示在螢幕上的像素是從紋理的紋素中計算的,計算方法由紋理濾波決定。最簡單的方法是每個像素使用一個最接近的紋素,多個紋素之間的線性插值也很常用,還有更複雜的辦法,參看Mipmap。另外,紋理也可用來決定模型表面的顏色,甚至雙向反射分布函式(BRDF),從而和光照模型等方法結合起來。

貼圖的影像大小,必須為方形(長方亦可),且其長寬的像素必須為2的倍數,這是為了節省硬體資源。如果不是所需大小,可以用縮放方式,或是填充空白像素來解決。




OPENGLES參考指令

GL_TEXTURE_WRAP_S,有三個次選項
1. GL_CLAMP_TO_EDGE, 2. GL_MIRRORED_REPEAT, 3. GL_REPEAT(default).  

GL_TEXTURE_WRAP_T,有三個次選項
1. GL_CLAMP_TO_EDGE, 2. GL_MIRRORED_REPEAT, 3. GL_REPEAT(default).


組合參考, GL_MIRRORED_REPEAT只是在重複時,會呈現鏡射狀。

WRAP_S: GL_REPEAT
WRAP_T: GL_REPEAT
WRAP_S: GL_REPEAT
WRAP_T: GL_CLAMP_TO_EDGE
WRAP_S: GL_REPEAT
WRAP_T: GL_CLAMP_TO_EDGE
WRAP_S: GL_CLAMP_TO_EDGE
WRAP_T: GL_CLAMP_TO_EDGE






glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);


其中
S = x dimension
T = y dimension
 
 
Many 3D modeling packages can export UV texture coordinates 
along with the model. 
(opengl calls these coordinates S and T). 

(U,V) = (X,Y) of texture.

U = X pixel position of image / image width ;

V = Y pixel position of image / image height ;

U/V類似一個相對於Texture這個圖上位置的百分比(假設圖寬及長各為一)


 
但是在iphone 3D Programming這本書上面寫的是左上系統,同DirectX所述。 
而OPENGL的規格所寫的是左下系統。 

只要知道差異就好了,不必細究太多,有問題的話再反轉一下Y就好了。
 

 參考網站

http://www.inf.ufrgs.br/~oliveira/RTM.html

http://zh.wikipedia.org/wiki/%E6%9D%90%E8%B4%A8%E8%B4%B4%E5%9B%BE

http://open.gl/textures

http://www.opengl.org/wiki/Texture

http://www.imobilebbs.com/wordpress/archives/1571

http://www.cs.nccu.edu.tw/~mtchi/3d12/lab/3d12-lab5.pdf

http://caobeixingqiu.is-programmer.com/posts/18999.html

http://thedev-log.blogspot.tw/2012/07/texture-coordinates-tutorial-opengl-and.html

2013年6月19日 星期三

UIImage 與 CGImage的區別

UIImage的本質上是一個便利包裝的CGImage

CGImage和CGImageRef这两个应当是用来重绘图形的类,它们在应用时是按照图像的像素矩阵来绘制图片的,它们可以用来处理bitmap。

CGImageRef与UIImage的互转
CGImageRef转换成UIImage CGImageRef
iOffscreen = CGBitmapContextCreateImage(context);
UIImage* image = [UIImage imageWithCGImage: iOffscreen];
 
 
UIImage转换成CGImageRef
UIImage *loadImage=[UIImage imageNamed:@"comicsplash.png"];
CGImageRef cgimage=loadImage.CGImage;

參考網頁
http://www.helmsmansoft.com/index.php/archives/1097


OpenGL基本實作(十二) (ObjViewer)

根據iphone 3D Programming所附的例子,重新根據新版的Xcode(4.6.2)改成iPad的例子。這個利字可以開啓兩個OBJ檔,然後顯示所附的3D Model的模樣。以下是實作經過。

1. 開啓一個專案




2. 將c math及GLSL檔從舊的檔案中載入,以下是檔案列表 


3. 將Models的檔案從原始例子中載入過來

因為程式所能分析的只能是v及f這兩種語法,因此所附的OBJ檔當然也是相對簡單的。如果真正從網路上去下載一個完整的OBJ檔進來,則會因為Parse Error而讓程式跳出。credits.txt則是原例子中說明下載的路徑,註冊後就可以下載來玩玩。網路上有很多OBJ格式的檔案,當然是完整的語法,所以無法在此例子中使用。


4. ApplicationEngine.ObjViewer.cpp

此例子是從原來ApplicationEngine.ParametricViewer.cpp修改,差別不大,主要是為了區隔,而改變檔名的。
#include "Interfaces.hpp"
#include "ObjSurface.hpp"
#include "ParametricEquations.hpp"

using namespace std;

namespace ObjViewer {

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, IResourceManager* resourceManager);
    ~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;
    IResourceManager* m_resourceManager;  // new
};
   
IApplicationEngine* CreateApplicationEngine(IRenderingEngine* renderingEngine, IResourceManager* resourceManager)
{
    return new ApplicationEngine(renderingEngine, resourceManager);
}

ApplicationEngine::ApplicationEngine(IRenderingEngine* renderingEngine, IResourceManager* resourceManager) :
    m_spinning(false), // 此處的用法如同  m_spinning = false
    m_pressedButton(-1),
    m_renderingEngine(renderingEngine),  // m_renderingEngine = renderingEngine ,這時只是一個空的Pointer
    m_resourceManager(resourceManager)  // m_resourceManager =  resourceManager  空的Pointer
{
    m_animation.Active = false;
    m_currentSurface = 5; //與前一個例子的稍稍不一樣的排列
    m_buttonSurfaces[0] = 1;
    m_buttonSurfaces[1] = 2;
    m_buttonSurfaces[2] = 3;
    m_buttonSurfaces[3] = 4;
    m_buttonSurfaces[4] = 0;
}

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);
    string path = m_resourceManager->GetResourcePath(); //主元件的路徑
    surfaces[0] = new ObjSurface(path + "/micronapalmv2.obj");
    //surfaces[1] = new ObjSurface(path + "/Ninja.obj");
    surfaces[1] = new ObjSurface(path + "/Trex.OBJ");
    surfaces[2] = new Torus(1.4, 0.3);
    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];  // 處理完後,資料已經放進renderingEngine中,因此就先釋放掉。
}

void ApplicationEngine::PopulateVisuals(Visual* visuals) const
{
    for (int buttonIndex = 0; buttonIndex < ButtonCount; buttonIndex++) {
       
        int visualIndex = m_buttonSurfaces[buttonIndex];
        visuals[visualIndex].Color = vec3(0.25f, 0.25f, 0.25f);
        if (m_pressedButton == buttonIndex)
            visuals[visualIndex].Color = vec3(0.5f, 0.5f, 0.5f);
       
        visuals[visualIndex].ViewportSize = m_buttonSize;
        visuals[visualIndex].LowerLeft.x = buttonIndex * m_buttonSize.x;
        visuals[visualIndex].LowerLeft.y = 0;
        visuals[visualIndex].Orientation = Quaternion();
    }
   
    visuals[m_currentSurface].Color = m_spinning ? vec3(1, 1, 0.5f) : vec3(0.25, 0.75, 1);
    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++) {
           
            const Visual& start = m_animation.StartingVisuals[i];
            const Visual& end = m_animation.EndingVisuals[i];
            Visual& tweened = visuals[i];
           
            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);
}



.....

}


5. GLView.h


#import "Interfaces.hpp"
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>

@interface GLView : UIView {
@private
    IApplicationEngine* m_applicationEngine;
    IRenderingEngine* m_renderingEngine;
    IResourceManager* m_resourceManager;  //new
    EAGLContext* m_context;
    float m_timestamp;
}

- (void) drawView: (CADisplayLink*) displayLink;

@end


6.  GLView.mm


#import "GLView.h"

#define GL_RENDERBUFFER 0x8d41

@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;
        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;
        }
       
        // m_resourceManager 作為一個界面去傳送資源(obj檔或貼圖檔...)的相關資料
        m_resourceManager = Darwin::CreateResourceManager();


        if (api == kEAGLRenderingAPIOpenGLES1) {
            NSLog(@"Using OpenGL ES 1.1");
            m_renderingEngine = SolidES1::CreateRenderingEngine();
        } else {
            NSLog(@"Using OpenGL ES 2.0");
            m_renderingEngine = SolidES2::CreateRenderingEngine();
        }

        m_applicationEngine = ObjViewer::CreateApplicationEngine(m_renderingEngine, m_resourceManager);

        [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

7. 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,
    VertexFlagsTexCoords = 1 << 1,
};

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() {}
};

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() {}
};

struct IResourceManager {
    virtual string GetResourcePath() const = 0;
    virtual void LoadPngImage(const string& filename) = 0;
    virtual void* GetImageData() = 0;
    virtual ivec2 GetImageSize() = 0;
    virtual void UnloadImage() = 0;
    virtual ~IResourceManager() {}
};

//namespace ParametricViewer { IApplicationEngine* CreateApplicationEngine(IRenderingEngine*); }
namespace ObjViewer    { IApplicationEngine* CreateApplicationEngine(IRenderingEngine*, IResourceManager*); } // ObjViewer有定義兩次namespace,此處作為function的宣告(Prototypes)

namespace Darwin       { IResourceManager* CreateResourceManager(); } // ResourceManager所用

//namespace WireframeES1 { IRenderingEngine* CreateRenderingEngine(); }
//namespace WireframeES2 { IRenderingEngine* CreateRenderingEngine(); }
namespace SolidES1     { IRenderingEngine* CreateRenderingEngine(); }
namespace SolidES2     { IRenderingEngine* CreateRenderingEngine(); }


8. ObjSurface.hpp 讀取OBJ檔的主程式定義檔,定義了處理的Class


#include "Interfaces.hpp"

class ObjSurface : public ISurface {
public:
    ObjSurface(const string& name);
    int GetVertexCount() const;
    int GetLineIndexCount() const { return 0; }
    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;
private:
    string m_name;  // OBJ檔名
    vector<ivec3> m_faces; // 紀錄STL的Array
    mutable size_t m_faceCount;  //  face (f)的數量
    mutable size_t m_vertexCount;  // vertex (v) 的數量
    static const int MaxLineSize = 128;  // 一行讀取的最大字數
};


9.  ObjSurface.hpp 讀取OBJ檔的主程式檔

#include "ObjSurface.hpp"
#import <list>
#import <fstream>
#import <assert.h>

using namespace std;

ObjSurface::ObjSurface(const string& name) :
    m_name(name),
    m_faceCount(0),
    m_vertexCount(0)
{
    m_faces.resize(GetTriangleIndexCount() / 3); // 設定m_faces的記憶體,三角形的面
    ifstream objFile(m_name.c_str());
    vector<ivec3>::iterator face = m_faces.begin();
    while (objFile) {
        char c = objFile.get();
        if (c == 'f') { //將f 面(Face)的資料一行行分析出來
            assert(face != m_faces.end() && "parse error");
           
            objFile >> face->x >> face->y >> face->z;
            // 將值射定到m_faces的陣列(vector)中,資料為vertex所對應點的代號/index
           
            *face++ -= ivec3(1, 1, 1); // m_faces記錄三角形面的三個點之index,因為起始點為0開始
        }
        objFile.ignore(MaxLineSize, '\n');
    }
    assert(face == m_faces.end() && "parse error");
}

int ObjSurface::GetVertexCount() const
{
    if (m_vertexCount != 0)
        return m_vertexCount;
   
    ifstream objFile(m_name.c_str());
    while (objFile) {
        char c = objFile.get();
        if (c == 'v')
            m_vertexCount++;
        objFile.ignore(MaxLineSize, '\n');
    }
    return m_vertexCount;
}

int ObjSurface::GetTriangleIndexCount() const
{
    if (m_faceCount != 0)
        return m_faceCount * 3; // 三角形成一個面
   
    ifstream objFile(m_name.c_str());
    while (objFile) {
        char c = objFile.get();
        if (c == 'f') // f 面(Face)的資料
            m_faceCount++; // 記錄資料行數(長度)
       
        // 而如果期间遇到"\n"(換行),则停止向前,定位在该处 ,在此處前面所讀都不處理
        objFile.ignore(MaxLineSize, '\n'); 
    }
    return m_faceCount * 3; //三角形的面,故乘三
}

void ObjSurface::GenerateVertices(vector<float>& floats, unsigned char flags) const
{
    // float 是Vertices 傳入的指標
   
    // 確認VertexFlagsNormals為1 這樣後續的處理才會進行
    assert(flags == VertexFlagsNormals && "Unsupported flags.");

    struct Vertex {
        vec3 Position;  // 放v的資料 v 幾何體頂點(Geometric vertices)
        vec3 Normal;    // 放該點的對外法向量
    };

    // Read in the vertex positions and initialize lighting normals to (0, 0, 0).
    floats.resize(GetVertexCount() * 6); //此處為兩個三角形的六個點,查iphone 3D Programming Fig4-3
    ifstream objFile(m_name.c_str());
    Vertex* vertex = (Vertex*) &floats[0];
    while (objFile) {
        char c = objFile.get();
        if (c == 'v') {  // 格式為 V  float1  float2  float3   \n
            vertex->Normal = vec3(0, 0, 0);  //default 法線為0
            vec3& position = (vertex++)->Position;
            objFile >> position.x >> position.y >> position.z;
        }
        objFile.ignore(MaxLineSize, '\n'); // 跳過128的char或是遇到分行符號就停止。
    }

    vertex = (Vertex*) &floats[0];
    for (size_t faceIndex = 0; faceIndex < m_faces.size(); ++faceIndex) {
        ivec3 face = m_faces[faceIndex];

        // Compute the facet normal.
        vec3 a = vertex[face.x].Position; // 利用m_faces找出這個面上各點的真實坐標
        vec3 b = vertex[face.y].Position;
        vec3 c = vertex[face.z].Position;
        vec3 facetNormal = (b - a).Cross(c - a); // 利用三個座標,找出法線的向量

        // Add the facet normal to the lighting normal of each adjoining vertex.
        vertex[face.x].Normal += facetNormal; // 該點所對應的法線相加後,就成為該點真正所對應的對外的法向量。
        vertex[face.y].Normal += facetNormal;
        vertex[face.z].Normal += facetNormal;
    }

    // Normalize the normals.
    for (int v = 0; v < GetVertexCount(); ++v)
        vertex[v].Normal.Normalize();
}

void ObjSurface::GenerateTriangleIndices(vector<unsigned short>& indices) const
{
    indices.resize(GetTriangleIndexCount());
    vector<unsigned short>::iterator index = indices.begin();
    for (vector<ivec3>::const_iterator f = m_faces.begin(); f != m_faces.end(); ++f) {
        *index++ = f->x;  // 三角形的三個點所對應的index,因為每一個face面為三角形
        *index++ = f->y;
        *index++ = f->z;
    }
}

 

10. ResourceManager.mm,作為資源管理,但本次只用到讀取檔案的路徑處理

#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#import <string>
#import <iostream>
#import "Interfaces.hpp"

using namespace std;

namespace Darwin {
   
class ResourceManager : public IResourceManager {
public:
    string GetResourcePath() const
    {
        NSString* bundlePath =[[NSBundle mainBundle] resourcePath];
        // 找到主原件 mainBundle的位置
        //simulator的位置 /Users/username/Library/Application Support/iPhone Simulator/User/Applications/uuid/ModelViewer.app
        // device的位置 /var/mobile/Applications/uuid/ModelViewer.app
       
        return [bundlePath UTF8String];  // 轉成C string object 給 C++ STL string
       
    }
    void LoadPngImage(const string& name)  // 本次Objviewer用不到
    {
        NSString* basePath = [[NSString alloc] initWithUTF8String:name.c_str()];
        NSBundle* mainBundle = [NSBundle mainBundle];
        NSString* fullPath = [mainBundle pathForResource:basePath ofType:@"png"];
        UIImage* uiImage = [[UIImage alloc] initWithContentsOfFile:fullPath];
        CGImageRef cgImage = uiImage.CGImage;
        m_imageSize.x = CGImageGetWidth(cgImage);
        m_imageSize.y = CGImageGetHeight(cgImage);
        m_imageData = CGDataProviderCopyData(CGImageGetDataProvider(cgImage));
        //[uiImage release];
        //[basePath release];
    }
    void* GetImageData()
    {
        return (void*) CFDataGetBytePtr(m_imageData);
    }
    ivec2 GetImageSize()
    {
        return m_imageSize;
    }
    void UnloadImage()
    {
        CFRelease(m_imageData);
    }
private:
    CFDataRef m_imageData;
    ivec2 m_imageSize;
};

IResourceManager* CreateResourceManager()
{
    return new ResourceManager();
}

}


11. 結果顯示,與原始iphone版本相同,此例作為瞭解3D檔案讀取的控制方式






2013年6月18日 星期二

static 的意義

1.用作變數:
1-1:放在函數內

Ex:

void MyFun()
{
static int i=0;
}

說明:"i"為"static,i"的生命週期會到程式結束,但是他的scope 還是在MyFun()中,每次呼叫MyFun(),函數中"i"的值會保留上次呼叫最後一次設定的值。

1-2:放在函數外

Ex:檔案 abc.c 中寫了如下

static int s_i=0;

int myfun()

{}

說明:此時"s_i"只能在檔案"abc.c"中使用,他可以讓"abc.c"中所有的"function"看到,他的生命週期也是到程式結束,但是別的.c 檔是無法使用到"abc.c"檔中的"s_i"。

2.在function 前面加上"static"

Ex:檔案 123.c 中寫了如下

static void MyFun()
{return;}

說明:此時"MyFun()"只能給這個123.c 這個檔案中的其他function 呼叫。
(簡單的說,就是"MyFun()"這個function不會被linker 參考到)





讓該變數(或該函式)的可視範圍只侷限在該檔案內,其他的 .c檔看不到此變數(或函式)的存在。
既使其他檔案用extern宣告也看不到!套句行話來說,他把Global的變數或函數變成了「internal linkage」,當Linker在找symbol時是會忽略它的。
(在C++中也相容這種用法,不過被視為比較不建議的舊的用法,C++比較建議使用unnamed namespace。)

使用時機:當此全域變數(或全域函式)不想被其他檔案引用和修改時,或者不同檔案可以使用相同名字的全域變數(或全域函式)而不會產生命名衝突。

做個總結,static的用法整理如下表:

修飾對象   使用在...    Linkage    Scope
---------------------------------
變數     函式裡       internal    Block
變數     函式外       internal    File
變數     成員        external     Class    (for C++)
函式     global       internal     File
函式     成員        external     Class    (for C++)


C語言及由其衍生出的C++Objective-C等程式語言中,「static」是用於控制變數的生命周期和連線方式(即其作用域,亦即可見性)的保留字。確切來說,正如C族語言中的extern,auto與register這些保留字一樣,static也是一種儲存類(此處的「類」與物件導向語言的「」的定義不同)標識。每個變數與函式都有以上的一種儲存類標識,如果在宣告中沒有明確標識其儲存類,編譯時就會根據上下文來選擇其預設儲存類,如在原始檔里的所有檔案級變數對應的預設儲存類是extern,而在函式體內的變數對應的則是auto,各儲存類的屬性如下表所列。
儲存類名 生命周期 作用域
extern 靜態(程式結束後釋放) 外部(整個程式)
static 靜態(程式結束後釋放) 內部(僅翻譯單元,一般指單個原始檔)
auto,register 函式呼叫(呼叫結束後釋放)





static的原始用意

1、什么是static?

       static 是C++中很常用的修饰符,它被用来控制变量的存储方式和可见性。
   

2、为什么要引入static?

       函数内部定义的变量,在程序执行到它的定义处时,编译器为它在栈上分配空间,大家知道,函数在栈上分配的空间在此函数执行结束时会释放掉,这样就产生了一 个问题: 如果想将函数中此变量的值保存至下一次调用时,如何实现? 最容易想到的方法是定义一个全局的变量,但定义为一个全局变量有许多缺点,最明显的缺点是破坏了此变量的访问范围(使得在此函数中定义的变量,不仅仅受此 函数控制)。
   

3、什么时候用static?

       需要一个数据对象为整个类而非某个对象服务,同时又力求不破坏类的封装性,即要求此成员隐藏在类的内部,对外不可见。


 4、static的内部机制:

       静态数据成员要在程序一开始运行时就必须存在。因为函数在程序运行中被调用,所以静态数据成员不能在任何函数内分配空间和初始化。
       这样,它的空间分配有三个可能的地方,一是作为类的外部接口的头文件,那里有类声明;二是类定义的内部实现,那里有类的成员函数定义;三是应用程序的main()函数前的全局数据声明和定义处。
      静态数据成员要实际地分配空间,故不能在类的声明中定义(只能声明数据成员)。类声明只声明一个类的“尺寸和规格”,并不进行实际的内存分配,所以在类声 明中写成定义是错误的。它也不能在头文件中类声明的外部定义,因为那会造成在多个使用该类的源文件中,对其重复定义。
      static被引入以告知编译器,将变量存储在程序的静态存储区而非栈上空间,静态
数据成员按定义出现的先后顺序依次初始化,注意静态成员嵌套时,要保证所嵌套的成员已经初始化了。消除时的顺序是初始化的反顺序。
   

5、static的优势:

       可以节省内存,因为它是所有对象所公有的,因此,对多个对象来说,静态数据成员只存储一处,供所有对象共用。静态数据成员的值对每个对象都是一样,但它的 值是可以更新的。只要对静态数据成员的值更新一次,保证所有对象存取更新后的相同的值,这样可以提高时间效率。
   

6、引用静态数据成员时,采用如下格式:

         <类名>::<静态成员名>
    如果静态数据成员的访问权限允许的话(即public的成员),可在程序中,按上述格式
来引用静态数据成员。
   

7、注意事项:

      (1)类的静态成员函数是属于整个类而非类的对象,所以它没有this指针,这就导致
了它仅能访问类的静态数据和静态成员函数。
      (2)不能将静态成员函数定义为虚函数。
      (3)由于静态成员声明于类中,操作于其外,所以对其取地址操作,就多少有些特殊
,变量地址是指向其数据类型的指针 ,函数地址类型是一个“nonmember函数指针”。
      (4)由于静态成员函数没有this指针,所以就差不多等同于nonmember函数,结果就
产生了一个意想不到的好处:成为一个callback函数,使得我们得以将C++和C-based X W
indow系统结合,同时也成功的应用于线程函数身上。
      (5)static并没有增加程序的时空开销,相反她还缩短了子类对父类静态成员的访问
时间,节省了子类的内存空间。
      (6)静态数据成员在<定义或说明>时前面加关键字static。
      (7)静态数据成员是静态存储的,所以必须对它进行初始化。
      (8)静态成员初始化与一般数据成员初始化不同:
      初始化在类体外进行,而前面不加static,以免与一般静态变量或对象相混淆;
      初始化时不加该成员的访问权限控制符private,public等;
           初始化时使用作用域运算符来标明它所属类;
           所以我们得出静态数据成员初始化的格式:
         <数据类型><类名>::<静态数据成员名>=<值>
      (9)为了防止父类的影响,可以在子类定义一个与父类相同的静态变量,以屏蔽父类的影响。这里有一点需要注意:我们说静态成员为父类和子类共享,但我们有 重复定义了静态成员,这会不会引起错误呢?不会,我们的编译器采用了一种绝妙的手法:name-mangling 用以生成唯一的标志。


静态数据成员


  在类中,静态成员可以实现多个对象之间的数据共享,并且使用静态数据成员还不会破坏隐藏的原则,即保证了安全性。因此,静态成员是类的所有对象中共享的成员,而不是某个对象的成员。

   使用静态数据成员可以节省内存,因为它是所有对象所公有的,因此,对多个对象来说,静态数据成员只存储一处,供所有对象共用。静态数据成员的值对每个对 象都是一样,但它的值是可以更新的。只要对静态数据成员的值更新一次,保证所有对象存取更新后的相同的值,这样可以提高时间效率。

  静态数据成员的使用方法和注意事项如下:

  1、静态数据成员在定义或说明时前面加关键字static。

  2、静态成员初始化与一般数据成员初始化不同。静态数据成员初始化的格式如下:

    <数据类型><类名>::<静态数据成员名>=<值>

  这表明:

        (1) 初始化在类体外进行,而前面不加static,以免与一般静态变量或对象相混淆。

  (2) 初始化时不加该成员的访问权限控制符private,public等。

  (3) 初始化时使用作用域运算符来标明它所属类,因此,静态数据成员是类的成员,而不是对象的成员。

  3、静态数据成员是静态存储的,它是静态生存期,必须对它进行初始化。

  4、引用静态数据成员时,采用如下格式:

   <类名>::<静态成员名>

  如果静态数据成员的访问权限允许的话(即public的成员),可在程序中,按上述格式来引用静态数据成员。
静态成员函数
  静态成员函数和静态数据成员一样,它们都属于类的静态成员,它们都不是对象成员。因此,对静态成员的引用不需要用对象名。

  在静态成员函数的实现中不能直接引用类中说明的非静态成员,可以引用类中说明的静态成员。如果静态成员函数中要引用非静态成员时,可通过对象来引用。






資料來源:

http://flykof.pixnet.net/blog/post/23583952

http://archerworks.blogspot.tw/2010/07/cstatic.html

http://blog.csdn.net/danforn/article/details/2312766

http://zh.wikipedia.org/wiki/%E9%9D%99%E6%80%81%E5%8F%98%E9%87%8F

The size_t confusion

size_t是一些C/C++标准在stddef.h中定义的。这个类型足以用来表示对象的大小。

size_t的真实类型与操作系统有关,在32位架构中被普遍定义为:

typedef unsigned int size_t;

而在64位架构中被定义为:

typedef unsigned long size_t;


size_t在32位架构上是4 word,在64位架构上是8 word,在不同架构上进行编译时需要注意这个问题。

而int在不同架构下都是4 word,与size_t不同;且int为带符号数,size_t为无符号数。

因為size_t 是為了給sizeof()這個function使用的,因此為了紀錄回傳值,特別用typedef制定了這一個變數。參考原文 如下

The C language provides the separate types size_t and ptrdiff_t to represent memory-related quantities. Existing types were deemed insufficient, because their size is defined according to the target processor's arithmetic capabilities, not the memory capabilities, such as available address space. Both of these types are defined in the stddef.h header (cstddef header in C++).

size_t is used to represent the size of any object (including arrays) in the particular implementation. It is used as the return type of the sizeof operator. The maximum size of size_t is provided via SIZE_MAX, a macro constant which is defined in the stdint.h header (cstdint header in C++). It is guaranteed to be at least 65535.

example:

#include<iostream>
#include<cstdlib> //Used for std::size_t ... on some compilers

#define u16 unsigned short
#define u32 unsigned //integer is implicit
int main()
{
  size_t size = sizeof(size_t);
  std::cout << "Size of size_t is " << size << std::endl;

  std::size_t size2 = sizeof(std::size_t);
  std::cout << "Size of std::size_t is " << size2 << std::endl;

  size_t size3 = sizeof(u16);
  std::cout << "Size of u16 is " << size3 << std::endl;

  size_t size4 = sizeof(u32);
  std::cout << "Size of u32 is " << size4 << std::endl;

  return 0;
}
 
 
參考網頁
http://witmax.cn/c-int-vs-size_t.html
http://en.wikipedia.org/wiki/C_data_types
http://advancedcppwithexamples.blogspot.tw/2010/04/sizet-confusion.html
 




3D立體影像顯示次產業

參考 http://rrs.moeasmea.gov.tw/Doc/document/3D%E7%AB%8B%E9%AB%94%E5%BD%B1%E5%83%8F%E9%A1%AF%E7%A4%BA%E6%AC%A1%E7%94%A2%E6%A5%AD-v1rf.htm

下圖為3D顯示產品的結構,其中包括了:應用內容創作、內容產生、處理/編碼、儲存傳輸、解碼、顯示六大項,專業整合型應用則整合了所有的技術在裡頭。內容的創作與產出是3D立體顯示產業發展成功與否的關鍵,而相關的影像處理/編碼到傳輸儲存,則成為促成內容推動產出的重要配合技術,而後端的顯示硬體產品亦都將以3D內容的優質呈現為發展之重要方向。




國內在廣告文創、數位相框、數位相機、影像處理、廣告看板、廣告燈箱、光學膜片、投影機等各個產業,皆有為數不少的廠商投入,這些都是潛在可以納入3D產業供應鏈之廠商,適合開發3D數位相框、3D立體燈箱、3D立體廣告看板。


 

C++中的mutable关键字

mutalbe的中文意思是“可变的,易变的”,跟constant(既C++中的const)是反义词。
    在C++中,mutable也是为了突破const的限制而设置的。被mutable修饰的变量,将永远处于可变的状态,即使在一个const函数中。

我们知道,如果类的成员函数不会改变对象的状态,那么这个成员函数一般会声明成const的。但是,有些时候,我们需要在const的函数里面修改一些跟类状态无关的数据成员,那么这个数据成员就应该被mutalbe来修饰。

class ClxTest
{
public:
    
void Output() const;
};
void ClxTest::Output() const
{
    cout 
<< "Output for test!" << endl;
}
void OutputTest(const ClxTest& lx)
{
    lx.Output();
}

类ClxTest的成员函数Output是用来输出的,不会修改类的状态,所以被声明为const的。
    函数OutputTest也是用来输出的,里面调用了对象lx的Output输出方法,为了防止在函数中调用其他成员函数修改任何成员变量,所以参数也被const修饰。
    如果现在,我们要增添一个功能:计算每个对象的输出次数。如果用来计数的变量是普通的变量的话,那么在const成员函数Output里面是不能修改该变 量的值的;而该变量跟对象的状态无关,所以应该为了修改该变量而去掉Output的const属性。这个时候,就该我们的mutable出场了——只要用mutalbe来修饰这个变量,所有问题就迎刃而解了。

class ClxTest
{
public:
    ClxTest();
    
~ClxTest();

    
void Output() const;
    
int GetOutputTimes() const;
private:
    mutable 
int m_iTimes;
};

ClxTest::ClxTest()
{
    m_iTimes 
= 0;
}

ClxTest::
~ClxTest()
{
}
void ClxTest::Output() const
{
    cout 
<< "Output for test!" << endl;
    m_iTimes
++;
}
int ClxTest::GetOutputTimes() const
{
    
return m_iTimes;
}
void OutputTest(const ClxTest& lx)
{
    cout 
<< lx.GetOutputTimes() << endl;

    lx.Output();

    cout 
<< lx.GetOutputTimes() << endl;
}
 
计数器m_iTimes被mutable修饰,那么它就可以突破const的限制,在被const修饰的函数里面也能被修改。

另一個例子

class SomeClass {
public:
    ....
    double increment() const { // 設定 const
        return _index++;  // 因為index被設為mutable 所以可以被改變
     }

private:
    ....
    mutable double _index;  // 設定成mutable
};


參考網頁
http://blog.csdn.net/starlee/article/details/1430387

http://www.dk101.com/Discuz/viewthread.php?tid=74466

http://rritw.com/a/bianchengyuyan/C__/20121109/250789.html

2013年6月17日 星期一

使用ifstream和getline读取文件内容

讀取OBJ檔時,因為是文字檔,需要參考一下

參考網頁
http://www.cnblogs.com/JCSU/articles/1190685.html



假设有一个叫 data.txt 的文件, 它包含以下内容:


Fry: One Jillion dollars.
[Everyone gasps.]
Auctioneer: Sir, that's not a number.
数据读取, 测试 。

以下就是基于 data.txt 的数据读取操作:


#include <iostream>
#include 
<fstream>
#include 
<string>

using namespace std;

//输出空行
void OutPutAnEmptyLine()
{
    cout
<<"\n";
}


//读取方式: 逐词读取, 词之间用空格区分
//read data from the file, Word BWord
//when used in this manner, we'll get space-delimited bits of text from the file
//but all of the whitespace that separated words (including newlines) was lost. 
void ReadDataFromFileWBW()
{
    ifstream fin(
"data.txt");
    
string s;
    
while( fin >> s )
    
{
        cout 
<< "Read from file: " << s << endl;
    }

}


//读取方式: 逐行读取, 将行读入字符数组, 行之间用回车换行区分
//If we were interested in preserving whitespace,
//we could read the file in Line-By-Line using the I/O getline() function.
void ReadDataFromFileLBLIntoCharArray()
{
    ifstream fin(
"data.txt");
    
const int LINE_LENGTH = 100;
    
char str[LINE_LENGTH];
    
while( fin.getline(str,LINE_LENGTH) )
    
{
        cout 
<< "Read from file: " << str << endl;
    }

}


//读取方式: 逐行读取, 将行读入字符串, 行之间用回车换行区分
//If you want to avoid reading into character arrays,
//you can use the C++ string getline() function to read lines into strings
void ReadDataFromFileLBLIntoString()
{
    ifstream fin(
"data.txt");
    
string s;
    
while( getline(fin,s) )
    
{
        cout 
<< "Read from file: " << s << endl;
    }

}


//带错误检测的读取方式
//Simply evaluating an I/O object in a boolean context will return false
//if any errors have occurred
void ReadDataWithErrChecking()
{
    
string filename = "dataFUNNY.txt";
    ifstream fin( filename.c_str());
    
if!fin )
    
{
        cout 
<< "Error opening " << filename << " for input" << endl;
        exit(
-1);
    }

}


int main()
{
    ReadDataFromFileWBW(); 
//逐词读入字符串 
    OutPutAnEmptyLine(); //输出空行

    ReadDataFromFileLBLIntoCharArray(); 
//逐词读入字符数组
    OutPutAnEmptyLine(); //输出空行

    ReadDataFromFileLBLIntoString(); 
//逐词读入字符串
    OutPutAnEmptyLine(); //输出空行

    ReadDataWithErrChecking(); 
//带检测的读取
    return 0;
}

输出结果为:
Read from file: Fry:
Read from file: One
Read from file: Jillion
Read from file: dollars.
Read from file: [Everyone
Read from file: gasps.]
Read from file: Auctioneer:
Read from file: Sir,
Read from file: that's
Read from file: not
Read from file: a
Read from file: number.
Read from file: 数据读取,
Read from file: 测试
Read from file: 。


Read from file: Fry: One Jillion dollars.
Read from file: [Everyone gasps.]
Read from file: Auctioneer: Sir, that's not a number.
Read from file: 数据读取, 测试 。

Read from file: Fry: One Jillion dollars.
Read from file: [Everyone gasps.]
Read from file: Auctioneer: Sir, that's not a number.
Read from file: 数据读取, 测试 。

Error opening  dataFUNNY.txt for input
Press any key to continue



另外如果讀取的是一個格式化的檔案,每一行需要換行符號
John 20 30 
mike 30 20 
 
方法1. get() 每次返回一個字符。
 
方法2. ignore(int,char) 跳過一定數量的某個字符, 
但你必須傳給它兩個參數。第一個是需要跳過的字符數。 
第二個是一個字符, 當遇到的時候就會停止。 例子,
fin.ignore(100, ‘\n’); 會跳過100個字符,或者不足100的時候,
跳過所有之前的字符,包括 ‘\n’。
 
方法3. peek() 返回文件中的下一個字符, 但並不實際讀取它。
所以如果你用peek() 查看下一個字符, 用get() 在peek()之後讀取,
會得到同一個字符, 然後移動文件計數器。
 
方法4. putback(char) 輸入字符, 一次一個, 到流中。 
 
 
參考程式碼 
vector<ivec3>::iterator face = m_faces.begin();
    while (objFile) {
        char c = objFile.get();
        if (c == 'f') { //將f 開頭的資料一行行分析出來
            assert(face != m_faces.end() && "parse error");
            objFile >> face->x >> face->y >> face->z;
            
        }
        objFile.ignore(MaxLineSize, '\n');
    }