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