1 /*---------------------------------------------------------------------------*
2   Project:  NintendoWare
3   File:     main.cpp
4 
5   Copyright (C)2009-2010 Nintendo Co., Ltd./HAL Laboratory, Inc.  All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain
8   proprietary information of Nintendo of America Inc. and/or Nintendo
9   Company Ltd., and are protected by Federal copyright law.  They may
10   not be disclosed to third parties or copied or duplicated in any form,
11   in whole or in part, without the prior written consent of Nintendo.
12 
13   $Revision: 24605 $
14  *---------------------------------------------------------------------------*/
15 
16 
17 //------------------------------------------------------------------
18 // デモ: PackedFont
19 //
20 // 概要
21 //   nw::font::PackedFont の構築と破棄のサンプルです。
22 //   フォントリソースを一旦全てメモリに読み込んで構築する方法と
23 //   逐次読み込みながら構築していく方法の二通りを示します。
24 //
25 // 操作
26 //   なし。
27 //
28 //------------------------------------------------------------------
29 
30 
31 #include <GLES2/gl2.h>
32 #include <GLES2/gl2ext.h>
33 #include <nn.h>
34 #include <nn/math.h>
35 #include <nn/fs.h>
36 #include <nw/assert.h>
37 #include <nw/font/font_WideTextWriter.h>
38 #include <nw/font/font_PackedFont.h>
39 #include <nw/demo.h>
40 
41 #include <algorithm>
42 
43 namespace
44 {
45 
46 /*
47     順次読み込み用の読み込みバッファサイズ。
48 
49     nw::font::PackedFont::GetRequireBufferSize() に指定するバッファのサイズは
50     nw::font::PackedFont::HEADER_SIZE 以上である必要があります。
51 */
52 const int DEMO_STREAMING_READ_BUFFER_SIZE   = 16 * 1024;
53 
54 const char DEMO_LOAD_GROUPS[]    = "ascii";
55     // ASCII 文字をロードする
56 const f32 DEMO_CACHE_RATE        = 0.3f;
57     // ロードするシートの 30 % 分のキャッシュを確保する
58 
59 //---------------------------------------------------------------------------
60 //! @brief      メモリを確保します。
61 //!
62 //! @param[in]  size      確保するメモリのサイズ。
63 //! @param[in]  alignment 確保するメモリのアライメント値。
64 //!
65 //! @return     確保したメモリへのポインタを返します。
66 //---------------------------------------------------------------------------
67 void*
MemAlloc(size_t size,u8 alignment=4)68 MemAlloc(
69     size_t  size,
70     u8      alignment = 4
71 )
72 {
73 #if defined(NW_PLATFORM_CTR)
74     return nw::demo::SimpleApp::Allocate(size, alignment);
75 #else
76     return nw::demo::Alloc(size, alignment);
77 #endif
78 }
79 
80 //---------------------------------------------------------------------------
81 //! @brief      デバイスメモリからメモリを確保します。
82 //!
83 //! @param[in]  size      確保するメモリのサイズ。
84 //! @param[in]  alignment 確保するメモリのアライメント値。
85 //!
86 //! @return     確保したメモリへのポインタを返します。
87 //---------------------------------------------------------------------------
88 void*
DevMemAlloc(size_t size,u8 alignment=4)89 DevMemAlloc(
90     size_t  size,
91     u8      alignment = 4
92 )
93 {
94 #if defined(NW_PLATFORM_CTR)
95     return nw::demo::SimpleApp::AllocateDeviceMemory(size, alignment);
96 #else
97     return nw::demo::Alloc(size, alignment);
98 #endif
99 }
100 
101 //---------------------------------------------------------------------------
102 //! @brief      メモリを解放します。
103 //!
104 //! @param[in]  memory 解放するメモリへのポインタ。
105 //---------------------------------------------------------------------------
106 void
MemFree(void * memory)107 MemFree(void* memory)
108 {
109 #if defined(NW_PLATFORM_CTR)
110     nw::demo::SimpleApp::Free(memory);
111 #else
112     nw::demo::Free(memory);
113 #endif
114 }
115 
116 //---------------------------------------------------------------------------
117 //! @brief      シェーダの初期化を行います。
118 //!
119 //! @param[in,out]  pResource 描画用リソースを管理するオブジェクトへのポインタ。
120 //---------------------------------------------------------------------------
121 void
InitShaders(nw::font::TextWriterResource * pResource)122 InitShaders(nw::font::TextWriterResource* pResource)
123 {
124     const wchar_t* shaders = NW_DEMO_FILE_PATH( NW_FONT_SHADERBINARY );
125     nn::fs::FileReader shaderReader(shaders);
126 
127     const u32 fileSize = (u32)shaderReader.GetSize();
128 
129     void* shaderBinary = MemAlloc(fileSize);
130 
131     s32 read = shaderReader.Read(shaderBinary, fileSize);
132     NN_ASSERT(read == fileSize);
133 
134     pResource->InitResource(shaderBinary, fileSize);
135 
136     MemFree(shaderBinary);
137 }
138 
139 //---------------------------------------------------------------------------
140 //! @brief      描画の初期設定を行います。
141 //---------------------------------------------------------------------------
142 void
InitDraw()143 InitDraw()
144 {
145     glClearColor(0.3f, 0.3f, 0.3f, 1.0f);
146 
147     glDisable(GL_CULL_FACE);            // カリングを無効
148     glDisable(GL_SCISSOR_TEST);         // シザー処理を無効
149     glDisable(GL_POLYGON_OFFSET_FILL);  // ポリゴンオフセットを無効
150 #if defined(NW_PLATFORM_CTR)
151     glDisable(GL_EARLY_DEPTH_TEST_DMP); // アーリーデプステストを無効
152 #endif
153     glDisable(GL_DEPTH_TEST);           // デプステストを無効
154     glDisable(GL_STENCIL_TEST);         // ステンシルテストを無効
155 
156     // カラーバッファの全ての成分を書き込み可
157     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
158 }
159 
160 //---------------------------------------------------------------------------
161 //! @brief      フォントリソースを一旦メモリに全て読み込んで
162 //!             PackedFont を構築します。
163 //!
164 //! @param[out] pFont       構築するフォントへのポインタ。
165 //! @param[in]  pFileReader ロードするフォントリソースを指すファイルストリーム。
166 //! @param[in]  glyphGroups メモリ上にロードするグリフグループ。
167 //! @param[in]  rate        確保するシートキャッシュの割合。
168 //!
169 //! @return     PackedFont構築の成否を返します。
170 //---------------------------------------------------------------------------
171 bool
InitFont(nw::font::PackedFont * pFont,nn::fs::FileReader * pFileReader,const char * glyphGroups,f32 rate)172 InitFont(
173     nw::font::PackedFont*   pFont,
174     nn::fs::FileReader*     pFileReader,
175     const char*             glyphGroups,
176     f32                     rate
177 )
178 {
179     bool bSuccess;
180 
181     // フォントリソースを全てメモリに読み込みます。
182     const u32 fileSize = (u32)pFileReader->GetSize();
183     if( fileSize <= 0 )
184     {
185         NN_LOG("fail to get FileStream size.");
186         return false;
187     }
188 
189     const u32 readBufferSize = fileSize;
190     void* readBuffer = MemAlloc(readBufferSize);
191     if (readBuffer == NULL)
192     {
193         NN_LOG("fail to allocate memory for resource buffer(%d byte).", readBufferSize);
194         return false;
195     }
196 
197     const int readSize = pFileReader->Read(readBuffer, readBufferSize);
198     if (readSize != readBufferSize)
199     {
200         MemFree(readBuffer);
201         NN_LOG("fail to read resource file.");
202         return false;
203     }
204 
205 
206     // フォントの構築に必要なバッファサイズを取得し、バッファを確保します。
207     // GetRequireBufferSize の第2引数にはロードするグリフグループを指定します。
208     // GetRequireBufferSize の第3引数には展開したシートをキャッシュする領域の割合を指定します。
209     // 割合は 0.0 ~ 1.0 の間で指定します。割合を小さくすれば必要なメモリ量は減りますが
210     // 多くの文字を描画する場合のパフォーマンスが悪化します。
211     // 割合を大きくすればその逆となります。
212     const u32 fontBufferSize = nw::font::PackedFont::GetRequireBufferSize(
213                                             readBuffer, glyphGroups, rate);
214     if (fontBufferSize == 0)
215     {
216         MemFree(readBuffer);
217         NN_LOG("fail to GetRequireBufferSize.");
218         return false;
219     }
220 
221     void* const fontBuffer = DevMemAlloc(fontBufferSize, nw::font::GlyphDataAlignment);
222     if (fontBuffer == NULL)
223     {
224         MemFree(readBuffer);
225         NW_WARNING(false, "fail to allocate memory for font buffer(%d byte).", fontBufferSize);
226         return false;
227     }
228 
229     // フォントを構築します。
230     // Construct の第4引数にはロードするグリフグループを指定します。
231     // fontBufferSize を求める時に使用したものと同じものを使用しなければなりません。
232     bSuccess = pFont->Construct(fontBuffer, fontBufferSize, readBuffer, glyphGroups);
233     NW_ASSERT(bSuccess);
234 
235 
236     //--- 既にフォントが構築されているか,リソースが不正な場合に失敗します。
237     if( ! bSuccess )
238     {
239         NN_LOG("fail to Construct.");
240         MemFree(fontBuffer);
241     }
242 
243     // raedBuffer は以降使用しないので解放する事ができます。
244     // fontBuffer は構築に成功した場合は解放してはいけません。
245     MemFree(readBuffer);
246 
247     // 構築に関与するサイズを Report します。
248     NN_LOG("InitFont:\n"
249              "  fileSize=%7lld readBufferSize=%7d fontBufferSize=%7d\n",
250              pFileReader->GetSize(), readBufferSize, fontBufferSize);
251 
252     return bSuccess;
253 }
254 
255 //---------------------------------------------------------------------------
256 //! @brief      フォントリソースを順次読み込みながら PackedFont を構築します。
257 //!
258 //! @param[out] pFont       構築するフォントへのポインタ。
259 //! @param[in]  pFileReader ロードするフォントリソースを指すファイルストリーム。
260 //! @param[in]  glyphGroups メモリ上にロードするグリフグループ。
261 //! @param[in]  rate        確保するシートキャッシュの割合。
262 //!
263 //! @return     PackedFont構築の成否を返します。
264 //---------------------------------------------------------------------------
265 bool
InitFontStreaming(nw::font::PackedFont * pFont,nn::fs::FileReader * pFileReader,const char * glyphGroups,f32 rate)266 InitFontStreaming(
267     nw::font::PackedFont*   pFont,
268     nn::fs::FileReader*     pFileReader,
269     const char*             glyphGroups,
270     f32                     rate
271 )
272 {
273     s32 readSize;
274     nw::font::PackedFont::ConstructContext context;
275     nw::font::PackedFont::ConstructResult ret = nw::font::PackedFont::CONSTRUCT_ERROR;
276 
277     // 逐次読み込み用のバッファを確保します。
278     const u32 readBufferSize = DEMO_STREAMING_READ_BUFFER_SIZE;
279 
280     // nw::font::PackedFont::GetRequireBufferSize() に指定するバッファのサイズは
281     // nw::font::PackedFont::HEADER_SIZE 以上である必要があります。
282     NN_ASSERT(readBufferSize >= nw::font::PackedFont::HEADER_SIZE);
283 
284     void* const readBuffer = MemAlloc(readBufferSize);
285     if (readBuffer == NULL)
286     {
287         NW_WARNING(false, "fail to allocate read buffer(%d byte).", readBufferSize);
288         return false;
289     }
290 
291     // フォントの構築に必要なバッファサイズを取得するため
292     // フォントリソースの先頭を読み取ります。
293     readSize = pFileReader->Read(readBuffer, nw::font::PackedFont::HEADER_SIZE);
294     if (readSize != nw::font::PackedFont::HEADER_SIZE)
295     {
296         MemFree(readBuffer);
297         NN_LOG("Fail to load %d bytes. Maybe too small bcfna.", nw::font::PackedFont::HEADER_SIZE);
298         return false;
299     }
300 
301 
302     // フォントの構築に必要なバッファサイズを取得し、バッファを確保します。
303     // GetRequireBufferSize の第2引数にはロードするグリフグループを指定します。
304     // GetRequireBufferSize の第3引数には展開したシートをキャッシュする領域の割合を指定します。
305     // 割合は 0.0 ~ 1.0 の間で指定します。割合を小さくすれば必要なメモリ量は減りますが
306     // 多くの文字を描画する場合のパフォーマンスが悪化します。
307     // 割合を大きくすればその逆となります。
308     const u32 fontBufferSize = nw::font::PackedFont::GetRequireBufferSize(
309                                             readBuffer, glyphGroups, rate);
310     if (fontBufferSize == 0)
311     {
312         MemFree(readBuffer);
313         NN_LOG("fail to GetRequireBufferSize.");
314         return false;
315     }
316 
317     void* fontBuffer = DevMemAlloc(fontBufferSize, nw::font::GlyphDataAlignment);
318     if (fontBuffer == NULL)
319     {
320         MemFree(readBuffer);
321         NN_LOG("fail to allocate memory for font buffer(%d byte).", fontBufferSize);
322         return false;
323     }
324 
325 
326     // フォントの構築を開始します。
327     // InitStreamingConstruct の第4引数にはロードするグリフグループを指定します。
328     // fontBufferSize を求める時に使用したものと同じものを使用しなければなりません。
329     pFont->InitStreamingConstruct(&context, fontBuffer, fontBufferSize, glyphGroups);
330 
331     // ファイルストリームから逐次リソースを読み取り、StreamingConstruct に渡していきます。
332     while (readSize > 0)
333     {
334         ret = pFont->StreamingConstruct(&context, readBuffer, static_cast<u32>(readSize));
335         if (ret == nw::font::PackedFont::CONSTRUCT_ERROR)
336         {
337             MemFree(fontBuffer);
338             MemFree(readBuffer);
339             NN_LOG("fail to StreamingConstruct.");
340             return false;
341         }
342 
343         readSize = pFileReader->Read(readBuffer, readBufferSize);
344     }
345 
346     if (readSize < 0)
347     {
348         // エラー
349         MemFree(fontBuffer);
350         MemFree(readBuffer);
351         NN_LOG("fail to read resource.");
352         return false;
353     }
354 
355     // リソース読み込み完了 / フォント構築も完了しているはず
356     NW_ASSERT(ret == nw::font::PackedFont::CONSTRUCT_FINISH);
357 
358     // raedBuffer は以降使用しないので解放する事ができます。
359     // fontBuffer は構築に成功した場合は解放してはいけません。
360     MemFree(readBuffer);
361 
362     // 構築に関与するサイズを Report します。
363     NN_LOG("InitFontStreaming:\n"
364              "  fileSize=%7lld readBufferSize=%7d fontBufferSize=%7d\n",
365              pFileReader->GetSize(), readBufferSize, fontBufferSize);
366 
367     return true;
368 }
369 
370 //---------------------------------------------------------------------------
371 //! @brief      PackedFontを破棄します。
372 //!
373 //! @param[in]  pFont           破棄するフォントへのポインタ。
374 //---------------------------------------------------------------------------
375 void
CleanupFont(nw::font::PackedFont * pFont)376 CleanupFont(nw::font::PackedFont* pFont)
377 {
378     // フォントを破棄します。
379     void* buffer = pFont->Destroy();
380 
381     // 構築済みフォントであれば構築時に割り当てたバッファへのポインタが返ってきます。
382     if( buffer != NULL )
383     {
384         MemFree(buffer);
385     }
386 
387     // Destroy 後は再度 Construct するまでフォントとして使用できません。
388 }
389 
390 //---------------------------------------------------------------------------
391 //! @brief      文字列表示用にモデルビュー行列と射影行列を設定します。
392 //!
393 //! @param[in]  textWriterResource TextWriterResourceオブジェクトへの参照。
394 //! @param[in]  width              画面の幅。
395 //! @param[in]  height             画面の高さ。
396 //---------------------------------------------------------------------------
397 void
SetupTextCamera(const nw::font::TextWriterResource & textWriterResource,int width,int height)398 SetupTextCamera(
399     const nw::font::TextWriterResource&
400                 textWriterResource,
401     int         width,
402     int         height
403 )
404 {
405 #if defined(NW_PLATFORM_CTR)
406     const GLsizei lcdWidth  = height;
407     const GLsizei lcdHeight = width;
408 #else
409     const GLsizei lcdWidth  = width;
410     const GLsizei lcdHeight = height;
411 #endif
412     glViewport(0, 0, lcdWidth, lcdHeight);
413 
414     // 射影行列を正射影に設定
415     {
416         // 左上原点とし、Y軸とZ軸の向きが逆になるように設定します。
417         nn::math::MTX44 proj;
418         f32 znear   =  0.0f;
419         f32 zfar    = -1.0f;
420         f32 t       =  0;
421         f32 b       =  static_cast<f32>(height);
422         f32 l       =  0;
423         f32 r       =  static_cast<f32>(width);
424         nn::math::MTX44OrthoPivot(&proj, l, r, b, t, znear, zfar, nn::math::PIVOT_UPSIDE_TO_TOP);
425         textWriterResource.SetProjectionMtx(proj);
426     }
427 
428     // モデルビュー行列を単位行列に設定
429     {
430         nn::math::MTX34 mv;
431         nn::math::MTX34Identity(&mv);
432         textWriterResource.SetViewMtx(mv);
433     }
434 }
435 
436 //---------------------------------------------------------------------------
437 //! @brief      ASCII文字列を描画します。
438 //!
439 //! @param[in]  pFont              フォントへのポインタ。
440 //! @param[in]  textWriterResource TextWriterResourceオブジェクトへの参照。
441 //! @param[in]  title              フォントオブジェクトの種類をあらわす文字列。
442 //---------------------------------------------------------------------------
443 void
DrawAscii(const nw::font::Font * pFont,nw::font::TextWriterResource * pTextWriterResource,const wchar_t * title)444 DrawAscii(
445     const nw::font::Font*           pFont,
446     nw::font::TextWriterResource*   pTextWriterResource,
447     const wchar_t*                  title
448 )
449 {
450     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
451 
452     nw::font::WideTextWriter writer;
453 
454     writer.SetFont(pFont);
455     writer.SetTextWriterResource(pTextWriterResource);
456     writer.SetupGX();
457     writer.SetCursor(0, 0);
458 
459     writer.Printf(L"DEMO: %ls\n", title);
460 
461     // 文字見本を表示
462     writer.Print(L"Glyph Sample:\n");
463     writer.MoveCursorX(5);
464     writer.Print(L" !\"#$%&'()*+,-./0123456789:;<=>?\n");
465     writer.Print(L"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_\n");
466     writer.Print(L"`abcdefghijklmnopqrstuvwxyz{|}~\n");
467 
468     glFlush();
469 }
470 
471 }   // namespace
472 
473 //---------------------------------------------------------------------------
474 //! @brief      サンプルのメイン関数です。
475 //---------------------------------------------------------------------------
476 void
nnMain()477 nnMain()
478 {
479     // os の初期化
480     nn::os::Initialize();
481 
482     // fs の初期化
483     nn::fs::Initialize();
484 
485     nw::demo::SimpleApp& demoApp = nw::demo::SimpleApp::GetInstance();
486 
487     demoApp.Initialize();
488 
489     nw::font::PackedFont fontNormal;
490     nw::font::PackedFont fontStream;
491 
492     // フォントの構築
493     {
494         nn::fs::FileReader fileReader(NW_DEMO_FILE_PATH(L"tahoma.bcfna"));
495 
496         fileReader.Seek(0, nn::fs::POSITION_BASE_BEGIN);
497         const bool bSuccess = InitFont(&fontNormal, &fileReader, DEMO_LOAD_GROUPS, DEMO_CACHE_RATE);
498         NW_ASSERTMSG(bSuccess, "Fail to load PackedFont by default.");
499 
500         fileReader.Seek(0, nn::fs::POSITION_BASE_BEGIN);
501         const bool bSuccessStream = InitFontStreaming(&fontStream, &fileReader, DEMO_LOAD_GROUPS, DEMO_CACHE_RATE);
502         NW_ASSERTMSG(bSuccessStream, "Fail to load PackedFont by streaming.");
503     }
504 
505     // 描画リソースの構築
506     nw::font::TextWriterResource textWriterResource;
507     InitShaders(&textWriterResource);
508 
509     textWriterResource.ActiveGlProgram();
510 
511     InitDraw();
512 
513     // フォント情報の表示
514     volatile bool loop = true;
515     while (loop)
516     {
517         demoApp.SetRenderingTarget(demoApp.DISPLAY0);
518         {
519             glBindFramebuffer(GL_FRAMEBUFFER, demoApp.GetFrameBufferObject());
520             SetupTextCamera(textWriterResource, demoApp.DISPLAY0_WIDTH, demoApp.DISPLAY0_HEIGHT);
521             DrawAscii(&fontNormal, &textWriterResource, L"PackedFont - Construct");
522         }
523 
524         demoApp.SetRenderingTarget(demoApp.DISPLAY1);
525         {
526             glBindFramebuffer(GL_FRAMEBUFFER, demoApp.GetFrameBufferObject());
527             SetupTextCamera(textWriterResource, demoApp.DISPLAY1_WIDTH, demoApp.DISPLAY1_HEIGHT);
528             DrawAscii(&fontStream, &textWriterResource, L"PackedFont - StreamingConstruct");
529         }
530 
531         demoApp.SwapBuffer(demoApp.DISPLAY_BOTH);
532     }
533 
534     // フォントの破棄
535     CleanupFont(&fontStream);
536     CleanupFont(&fontNormal);
537 }
538