Last update: 2014/06/04
ここでは、iOSアプリのプロジェクト作成から Live2D モデルを表示するまでの手順を説明します。
◆用意するもの - Xcode (ここではXcode 5.1.1を使用します)
- Live2D ライブラリ
(ダウンロードしたLive2D SDK の「lib」フォルダにある「Debug-iphoneos」、「Debug-iphonesimulator」、「Release-iphoneos」、「Release-iphonesimulator」フォルダ。 ここでは、1.0.02のバージョンを使用します)
本来は、Live2Dのリソースは作成するモデルに応じてそれぞれ準備します。
ここではサンプルプロジェクトの「ハル」のリソースを使って説明をしていきます。
/sample/simple/res/haru/
- haru.moc
- haru.1024/haru.1024/texture_00.png
- haru.1024/haru.1024/texture_01.png
- haru.1024/haru.1024/texture_02.png
◆プロジェクトの作成と下準備
新規プロジェクトを作成して、必要なファイルをインポートしていきます。
Xcodeを起動して、メニューバーの「File」 > 「New」 > 「Project...」をクリックします。
|
作成するプロジェクトを聞かれるので、「Single View Application」を選択して「Next」をクリックします。
プロジェクト名などを設定して「Next」をクリックし、保存場所を指定して「Create」をクリックします。 ここでは、Product Nameを「Live2DSample」とします。 次に、必要なファイルをプロジェクトに追加していきましょう。
まずはライブラリやモデルファイルなどを入れるグループを作成します。
Xcode上でプロジェクトのルートで右クリックし、「New Group」をクリックします。
ここでは、グループ名は「Resources」とします。 Live2D SDKの、/sample/simple/res の中にある、モデルファイルとテクスチャを、 先ほど作成した Resources グループにドラッグアンドドロップします。
SDKの/lib内のライブラリ、/include内のヘッダファイルも同様に追加します。
ライブラリなどの追加が終わったら、追加したライブラリを使えるように設定しましょう。
プロジェクトから、「Build Settings」 > 「Linking」 > 「Other Linker Flags」 に、-lLive2D と記述します。
※-lLive2D の "l" は小文字のLです。大文字の I (アイ)や | (バーティカルバー)ではありませんので注意してください。
プロジェクトから「Build Settings」 > 「Search Paths」 > 「Library Search Paths」 には、ライブラリまでのパスを、 「Build Settings」 > 「Search Paths」 > 「User Header Search Paths」には、 ライブラリのヘッダファイル(上で追加した「include」フォルダ内のファイル)までのパスを設定します。
ここでは、「Library Search Paths」に $(SRCROOT)/$(CONFIGURATION)-$(PLATFORM_NAME) 、 「User Header Search Paths」に $(SRCROOT)/include と設定します。 ・$(SRCROOT) : プロジェクトまでのフルパス ・$(CONFIGURATION) : デバッグとリリースのどちらでビルドされるか(Debug、Release) ・$(PLATFORM_NAME) : プラットフォームの名前(iPhoneosなど)
最後に、「Build Settings」 > 「Apple LLVM 5.0 - Preprocessing」 > 「Preprocessor Macros」に、
L2D_TARGET_IPHONE を追加すれば、ライブラリを使用する準備は完了です。
◆OpenGLの設定
次はLive2Dモデルを表示させるための、OpenGLの設定を行います。
プロジェクト内の Live2DSample グループを右クリックして、「New File...」をクリック。 Objective-C class を選択して「Next」をクリック、クラス名を設定して「Next」をクリック、 ファイルの保存場所を指定して「Create」をクリックします。 ここでは、クラス名を「EAGLView」とし、SubclassはUIViewとします。
また、プロジェクト作成時に自動生成されたAppDelegate.m、ViewController.m、上で作成したEAGLView.m の拡張子を、 ".m" から ".mm" に変えます。 Live2Dのライブラリは、C++で記述されているため、iPhoneで利用するには 拡張子が .cpp (C++用拡張子)もしくは .mm ( Objective-CとC++を混在できる拡張子) である必要があります。
それではOpenGLの設定をしていきます。 作成した EAGLView.h を開き、以下のように書き換えます。
#import <UIKit/UIKit.h> #import <QuartzCore/QuartzCore.h> #import <OpenGLES/EAGL.h> #import <OpenGLES/ES1/gl.h> #import <OpenGLES/ES1/glext.h> @interface EAGLView : UIView { @private EAGLContext* context; GLint deviceWidth, deviceHeight; GLuint defaultFramebuffer, colorRenderbuffer; } - (id)initWithFrame:(CGRect)frame; - (void) drawView:(id)sender; - (void) dealloc; @end
EAGLView.mm を開き、initWithFrame 関数を以下のように書き換え、OpenGLを初期化します。
- (id)initWithFrame:(CGRect)frame { if ((self = [super initWithFrame:frame])) { CAEAGLLayer* eaglLayer = (CAEAGLLayer*)self.layer; self.contentScaleFactor = [UIScreen mainScreen].scale ; eaglLayer.opaque = TRUE; context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1]; if (!context || ![EAGLContext setCurrentContext:context]) { [self release]; return nil; } glGenFramebuffersOES(1, &defaultFramebuffer); glGenRenderbuffersOES(1, &colorRenderbuffer); glBindFramebufferOES(GL_FRAMEBUFFER_OES, defaultFramebuffer); glBindRenderbufferOES(GL_RENDERBUFFER_OES, colorRenderbuffer); glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, colorRenderbuffer); } return self; }
同じく EAGLView.mm に、 layoutSubViews 関数を追加し、以下のように書き換えます。
- - (void) layoutSubviews
- {
- [context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(CAEAGLLayer*)self.layer];
- glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &deviceWidth);
- glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &deviceHeight);
-
- glViewport(0, 0, deviceWidth, deviceHeight);
-
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
-
- float modelWidth = live2DModel->getCanvasWidth();
- glOrthof(
- 0,
- modelWidth,
modelWidth * deviceHeight / deviceWidth, - 0,
- 0.5f, -0.5f
- );
- }
次に、モデルの初期化を行います。 EAGLView.h に、Live2Dモデルとテクスチャの変数、テクスチャ作成用の関数の宣言を、 EAGLView.mm に、その実装を追加します。
- @interface EAGLView : UIView
- {
- @private
- live2d::Live2DModelIPhone* live2DModel;// Live2Dモデル
NSMutableArray* textures;// テクスチャ -
- EAGLContext* context;
-
- GLint deviceWidth, deviceHeight;
- GLuint defaultFramebuffer, colorRenderbuffer;
- }
- - (id)initWithFrame:(CGRect)frame;
- - (void) drawView:(id)sender;
- (void) dealloc; - - (GLuint)loadTexture:(NSString*)fileNamel;// OpenGL用のテクスチャを作成する関数
- @end
EAGLView.mm の initWithFrame 関数で、Live2Dのモデルを初期化します。
- - (id)initWithFrame:(CGRect)frame
- {
if ((self = [super initWithFrame:frame])) { -
- CAEAGLLayer* eaglLayer = (CAEAGLLayer*)self.layer;
- self.contentScaleFactor = [UIScreen mainScreen].scale ;
- eaglLayer.opaque = TRUE;
- context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
-
- if (!context || ![EAGLContext setCurrentContext:context])
- {
[self release]; - return nil;
- }
- glGenFramebuffersOES(1, &defaultFramebuffer);
- glGenRenderbuffersOES(1, &colorRenderbuffer);
glBindFramebufferOES(GL_FRAMEBUFFER_OES, defaultFramebuffer); - glBindRenderbufferOES(GL_RENDERBUFFER_OES, colorRenderbuffer);
- glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, colorRenderbuffer);
-
- // Live2Dモデルの初期化
Live2D::init() ; -
- NSString* modelpath = [[NSBundle mainBundle] pathForResource:MODEL_PATH ofType:@"moc"];
- live2DModel = Live2DModelIPhone::loadModel( [modelpath UTF8String] ) ;
-
- for( int i = 0 ; TEXTURE_PATH[i] != NULL ; i++ )
- {
- int texNo = [self loadTexture:(TEXTURE_PATH[i])] ;
- live2DModel->setTexture( i , texNo ) ;
[textures addObject:[NSNumber numberWithInt:texNo]];// 解放するために設定 - }
- }
- return self;
- }
EAGLView.mmに、画像からOpenGL用のテクスチャを作成する関数を追加します。
- (GLuint)loadTexture:(NSString*)fileName { GLuint texture; UIImage* uiImage = [UIImage imageNamed:fileName]; CGImageRef image = uiImage.CGImage ; size_t width = CGImageGetWidth(image); size_t height = CGImageGetHeight(image); GLubyte* imageData = (GLubyte*) calloc(width * height * 4 , 1); CGContextRef imageContext = CGBitmapContextCreate(imageData,width,height,8,width * 4,CGImageGetColorSpace(image), kCGImageAlphaPremultipliedLast); CGContextDrawImage(imageContext, CGRectMake(0, 0, (CGFloat)width, (CGFloat)height), image); CGContextRelease(imageContext); glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData); free(imageData); return texture; }
毎フレームの描画を行う関数を作成します。 EAGLView.mm に drawView 関数を追加し、以下のようにします。
- - (void) drawView:(id)sender
- {
- [EAGLContext setCurrentContext:context];
-
- glMatrixMode(GL_MODELVIEW);
- glLoadIdentity();
- glClear(GL_COLOR_BUFFER_BIT);
- glEnable(GL_BLEND);
glBlendFunc(GL_ONE , GL_ONE_MINUS_SRC_ALPHA ); - glDisable(GL_DEPTH_TEST) ;
- glDisable(GL_CULL_FACE) ;
-
- live2DModel->update();
live2DModel->draw(); -
- [context presentRenderbuffer:GL_RENDERBUFFER_OES];
- }
layoutSubViews 関数の一番最後に以下のように記述し、 drawView 関数が毎フレーム呼び出されるよう設定します。
- [NSTimer scheduledTimerWithTimeInterval:(NSTimeInterval)(1/60)
- target:self
- selector:@selector(drawView:)
- userInfo:nil repeats:TRUE];
最後に、ビューコントローラーの awakeFromNib 関数を以下のように書き換えて完了です!
ViewController.h - @interface ViewController : UIViewController{
- UIView* glView;
}
ViewController.m
- - (void)awakeFromNib
- {
- CGRect rect = CGRectMake(
- 0,
0, - [[UIScreen mainScreen] bounds].size.width,
- [[UIScreen mainScreen] bounds].size.height ) ;
- glView = [[EAGLView alloc] initWithFrame:rect] ;
-
[self.view addSubview:glView] ; - }
最終的なコードは以下のようになります。 以下のもの以外は、自動生成された状態から手を加えていません。
ViewController.h #import <UIKit/UIKit.h> @interface ViewController : UIViewController{ UIView* glView; } @end
ViewController.m
#import "ViewController.h" #import "EAGLView.h" @implementation ViewController - (void)awakeFromNib { CGRect rect = CGRectMake( 0, 0, [[UIScreen mainScreen] bounds].size.width, [[UIScreen mainScreen] bounds].size.height ) ; glView = [[EAGLView alloc] initWithFrame:rect] ; [self.view addSubview:glView] ; } - (void)viewDidLoad { [super viewDidLoad]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; } @end
EAGLView.h #import <UIKit/UIKit.h> #import <QuartzCore/QuartzCore.h> #import <OpenGLES/EAGL.h> #import <OpenGLES/ES1/gl.h> #import <OpenGLES/ES1/glext.h> #import "Live2DModelIPhone.h" @interface EAGLView : UIView { @private live2d::Live2DModelIPhone* live2DModel; NSMutableArray* textures; EAGLContext* context; GLint deviceWidth, deviceHeight; GLuint defaultFramebuffer, colorRenderbuffer; } - (id)initWithFrame:(CGRect)frame; - (void)drawView:(id)sender; - (GLuint)loadTexture:(NSString*)fileNamel; @end
EAGLView.mm #import "EAGLView.h" #import "Live2D.h" #import "UtSystem.h" using namespace live2d ; @implementation EAGLView NSString* MODEL_PATH = @"haru" ; NSString* TEXTURE_PATH[] = { @"texture_00.png" , @"texture_01.png" , @"texture_02.png" , NULL } ; + (Class) layerClass { return [CAEAGLLayer class]; } - (id)initWithFrame:(CGRect)frame { if ((self = [super initWithFrame:frame])) { CAEAGLLayer* eaglLayer = (CAEAGLLayer*)self.layer; self.contentScaleFactor = [UIScreen mainScreen].scale ; eaglLayer.opaque = TRUE; context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1]; if (!context || ![EAGLContext setCurrentContext:context]) { return nil; } glGenFramebuffersOES(1, &defaultFramebuffer); glGenRenderbuffersOES(1, &colorRenderbuffer); glBindFramebufferOES(GL_FRAMEBUFFER_OES, defaultFramebuffer); glBindRenderbufferOES(GL_RENDERBUFFER_OES, colorRenderbuffer); glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, colorRenderbuffer); Live2D::init() ; NSString* modelpath = [[NSBundle mainBundle] pathForResource:MODEL_PATH ofType:@"moc"]; live2DModel = Live2DModelIPhone::loadModel( [modelpath UTF8String] ) ; for( int i = 0 ; TEXTURE_PATH[i] != NULL ; i++ ) { int texNo = [self loadTexture:(TEXTURE_PATH[i])] ; live2DModel->setTexture( i , texNo ) ;// テクスチャとモデルを結びつける [textures addObject:[NSNumber numberWithInt:texNo]];// 解放するために設定 } } return self; } - (void) drawView:(id)sender { [EAGLContext setCurrentContext:context]; // モデル用のOpenGLの描画関係を設定 glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glClear(GL_COLOR_BUFFER_BIT); glEnable(GL_BLEND); glBlendFunc(GL_ONE , GL_ONE_MINUS_SRC_ALPHA ); glDisable(GL_DEPTH_TEST) ; glDisable(GL_CULL_FACE) ; live2DModel->update(); live2DModel->draw(); [context presentRenderbuffer:GL_RENDERBUFFER_OES]; } - (void) layoutSubviews { [context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(CAEAGLLayer*)self.layer]; glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &deviceWidth); glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &deviceHeight); // ビューポートはデバイスの幅と合わせる。画面全体に表示される。 glViewport(0, 0, deviceWidth, deviceHeight); // 簡易的にプロジェクション行列一つですべての変換を行う。 glMatrixMode(GL_PROJECTION); glLoadIdentity(); float modelWidth = live2DModel->getCanvasWidth(); // モデラーで設定したキャンバス幅 // 描画範囲の設定。引数はleft, right, bottom, top の順。 glOrthof( 0, modelWidth, modelWidth * deviceHeight / deviceWidth, 0, 0.5f, -0.5f ); [NSTimer scheduledTimerWithTimeInterval:(NSTimeInterval)(1/60) target:self selector:@selector(drawView:) userInfo:nil repeats:TRUE]; } // 画像ファイルからテクスチャを作成 - (GLuint)loadTexture:(NSString*)fileName { GLuint texture; // 画像ファイルを展開しCGImageRefを生成 UIImage* uiImage = [UIImage imageNamed:fileName]; CGImageRef image = uiImage.CGImage ; // 画像の大きさを取得 size_t width = CGImageGetWidth(image); size_t height = CGImageGetHeight(image); // ビットマップデータを用意 GLubyte* imageData = (GLubyte*) calloc(width * height * 4 , 1); CGContextRef imageContext = CGBitmapContextCreate(imageData,width,height,8,width * 4,CGImageGetColorSpace(image), kCGImageAlphaPremultipliedLast); CGContextDrawImage(imageContext, CGRectMake(0, 0, (CGFloat)width, (CGFloat)height), image); CGContextRelease(imageContext); // OpenGL用のテクスチャを生成 glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData); free(imageData); // 作成したテクスチャを返す return texture; } @end
|