BsExampleFramework.h 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. #pragma once
  2. #include "BsPrerequisites.h"
  3. #include "Reflection/BsRTTIType.h"
  4. #include "Resources/BsResources.h"
  5. #include "Resources/BsResourceManifest.h"
  6. #include "Mesh/BsMesh.h"
  7. #include "Importer/BsImporter.h"
  8. #include "Importer/BsMeshImportOptions.h"
  9. #include "Importer/BsTextureImportOptions.h"
  10. #include "BsExampleConfig.h"
  11. #include "Text/BsFontImportOptions.h"
  12. #include "FileSystem/BsFileSystem.h"
  13. #include "Input/BsVirtualInput.h"
  14. namespace bs
  15. {
  16. /** A list of mesh assets provided with the example projects. */
  17. enum class ExampleMesh
  18. {
  19. Pistol,
  20. Cerberus
  21. };
  22. /** A list of texture assets provided with the example projects. */
  23. enum class ExampleTexture
  24. {
  25. PistolAlbedo,
  26. PistolNormal,
  27. PistolRoughness,
  28. PistolMetalness,
  29. EnvironmentPaperMill,
  30. GUIBansheeIcon,
  31. GUIExampleButtonNormal,
  32. GUIExampleButtonHover,
  33. GUIExampleButtonActive,
  34. DroneAlbedo,
  35. DroneNormal,
  36. DroneRoughness,
  37. DroneMetalness,
  38. GridPattern,
  39. GridPattern2,
  40. EnvironmentDaytime,
  41. EnvironmentRathaus,
  42. CerberusAlbedo,
  43. CerberusNormal,
  44. CerberusRoughness,
  45. CerberusMetalness,
  46. ParticleSmoke,
  47. DecalAlbedo,
  48. DecalNormal
  49. };
  50. /** A list of shader assets provided with the example projects. */
  51. enum class ExampleShader
  52. {
  53. CustomVertex,
  54. CustomDeferredSurface,
  55. CustomDeferredLighting,
  56. CustomForward
  57. };
  58. /** A list of font assets provided with the example projects. */
  59. enum class ExampleFont
  60. {
  61. SegoeUILight,
  62. SegoeUISemiBold
  63. };
  64. /** A list of assets without a speccific type provided with the example projects. */
  65. enum class ExampleResource
  66. {
  67. VectorField
  68. };
  69. /** Various helper functionality used throught the examples. */
  70. class ExampleFramework
  71. {
  72. public:
  73. /** Loads a manifest of all resources that were previously saved using this class. */
  74. static void loadResourceManifest()
  75. {
  76. const Path dataPath = EXAMPLE_DATA_PATH;
  77. const Path manifestPath = dataPath + "ResourceManifest.asset";
  78. if (FileSystem::exists(manifestPath))
  79. manifest = ResourceManifest::load(manifestPath, dataPath);
  80. else
  81. manifest = ResourceManifest::create("ExampleAssets");
  82. gResources().registerResourceManifest(manifest);
  83. }
  84. /** Saves the current resource manifest. */
  85. static void saveResourceManifest()
  86. {
  87. const Path dataPath = EXAMPLE_DATA_PATH;
  88. const Path manifestPath = dataPath + "ResourceManifest.asset";
  89. if(manifest)
  90. ResourceManifest::save(manifest, manifestPath, dataPath);
  91. }
  92. /** Registers a common set of keys/buttons that are used for controlling the examples. */
  93. static void setupInputConfig()
  94. {
  95. // Register input configuration
  96. // bsf allows you to use VirtualInput system which will map input device buttons and axes to arbitrary names,
  97. // which allows you to change input buttons without affecting the code that uses it, since the code is only
  98. // aware of the virtual names. If you want more direct input, see Input class.
  99. auto inputConfig = gVirtualInput().getConfiguration();
  100. // Camera controls for buttons (digital 0-1 input, e.g. keyboard or gamepad button)
  101. inputConfig->registerButton("Forward", BC_W);
  102. inputConfig->registerButton("Back", BC_S);
  103. inputConfig->registerButton("Left", BC_A);
  104. inputConfig->registerButton("Right", BC_D);
  105. inputConfig->registerButton("Forward", BC_UP);
  106. inputConfig->registerButton("Back", BC_DOWN);
  107. inputConfig->registerButton("Left", BC_LEFT);
  108. inputConfig->registerButton("Right", BC_RIGHT);
  109. inputConfig->registerButton("FastMove", BC_LSHIFT);
  110. inputConfig->registerButton("RotateObj", BC_MOUSE_LEFT);
  111. inputConfig->registerButton("RotateCam", BC_MOUSE_RIGHT);
  112. // Camera controls for axes (analog input, e.g. mouse or gamepad thumbstick)
  113. // These return values in [-1.0, 1.0] range.
  114. inputConfig->registerAxis("Horizontal", VIRTUAL_AXIS_DESC((UINT32)InputAxis::MouseX));
  115. inputConfig->registerAxis("Vertical", VIRTUAL_AXIS_DESC((UINT32)InputAxis::MouseY));
  116. }
  117. /**
  118. * Loads one of the builtin mesh assets. If the asset doesn't exist, the mesh will be re-imported from the source
  119. * file, and then saved so it can be loaded on the next call to this method.
  120. *
  121. * Use the 'scale' parameter to control the size of the mesh. Note this option is only relevant when a mesh is
  122. * being imported (i.e. when the asset file is missing).
  123. */
  124. static HMesh loadMesh(ExampleMesh type, float scale = 1.0f)
  125. {
  126. // Map from the enum to the actual file path
  127. static Path assetPaths[] =
  128. {
  129. Path(EXAMPLE_DATA_PATH) + "Pistol/Pistol01.fbx",
  130. Path(EXAMPLE_DATA_PATH) + "Cerberus/Cerberus.FBX",
  131. };
  132. const Path& srcAssetPath = assetPaths[(UINT32)type];
  133. // Attempt to load the previously processed asset
  134. Path assetPath = srcAssetPath;
  135. assetPath.setExtension(srcAssetPath.getExtension() + ".asset");
  136. HMesh model = gResources().load<Mesh>(assetPath);
  137. if (model == nullptr) // Mesh file doesn't exist, import from the source file.
  138. {
  139. // When importing you may specify optional import options that control how is the asset imported.
  140. SPtr<ImportOptions> meshImportOptions = Importer::instance().createImportOptions(srcAssetPath);
  141. // rtti_is_of_type checks if the import options are of valid type, in case the provided path is pointing to a
  142. // non-mesh resource. This is similar to dynamic_cast but uses Banshee internal RTTI system for type checking.
  143. if (rtti_is_of_type<MeshImportOptions>(meshImportOptions))
  144. {
  145. MeshImportOptions* importOptions = static_cast<MeshImportOptions*>(meshImportOptions.get());
  146. importOptions->setImportScale(scale);
  147. }
  148. model = gImporter().import<Mesh>(srcAssetPath, meshImportOptions);
  149. // Save for later use, so we don't have to import on the next run.
  150. gResources().save(model, assetPath, true);
  151. // Register with manifest, if one is present. Manifest allows the engine to find the resource even after
  152. // the application was restarted, which is important if resource was referenced in some serialized object.
  153. if(manifest)
  154. manifest->registerResource(model.getUUID(), assetPath);
  155. }
  156. return model;
  157. }
  158. /**
  159. * Loads one of the builtin texture assets. If the asset doesn't exist, the texture will be re-imported from the
  160. * source file, and then saved so it can be loaded on the next call to this method.
  161. *
  162. * Textures not in sRGB space (e.g. normal maps) need to be specially marked by setting 'isSRGB' to false. Also
  163. * allows for conversion of texture to cubemap by setting the 'isCubemap' parameter. If the data should be imported
  164. * in a floating point format, specify 'isHDR' to true. Note these options are only relevant when a texture is
  165. * being imported (i.e. when asset file is missing). If 'mips' is true, mip-map levels will be generated.
  166. */
  167. static HTexture loadTexture(ExampleTexture type, bool isSRGB = true, bool isCubemap = false, bool isHDR = false,
  168. bool mips = true)
  169. {
  170. // Map from the enum to the actual file path
  171. static Path assetPaths[] =
  172. {
  173. Path(EXAMPLE_DATA_PATH) + "Pistol/Pistol_DFS.png",
  174. Path(EXAMPLE_DATA_PATH) + "Pistol/Pistol_NM.png",
  175. Path(EXAMPLE_DATA_PATH) + "Pistol/Pistol_RGH.png",
  176. Path(EXAMPLE_DATA_PATH) + "Pistol/Pistol_MTL.png",
  177. Path(EXAMPLE_DATA_PATH) + "Environments/PaperMill_E_3k.hdr",
  178. Path(EXAMPLE_DATA_PATH) + "GUI/BansheeIcon.png",
  179. Path(EXAMPLE_DATA_PATH) + "GUI/ExampleButtonNormal.png",
  180. Path(EXAMPLE_DATA_PATH) + "GUI/ExampleButtonHover.png",
  181. Path(EXAMPLE_DATA_PATH) + "GUI/ExampleButtonActive.png",
  182. Path(EXAMPLE_DATA_PATH) + "MechDrone/Drone_diff.jpg",
  183. Path(EXAMPLE_DATA_PATH) + "MechDrone/Drone_normal.jpg",
  184. Path(EXAMPLE_DATA_PATH) + "MechDrone/Drone_rough.jpg",
  185. Path(EXAMPLE_DATA_PATH) + "MechDrone/Drone_metal.jpg",
  186. Path(EXAMPLE_DATA_PATH) + "Grid/GridPattern.png",
  187. Path(EXAMPLE_DATA_PATH) + "Grid/GridPattern2.png",
  188. Path(EXAMPLE_DATA_PATH) + "Environments/daytime.hdr",
  189. Path(EXAMPLE_DATA_PATH) + "Environments/rathaus.hdr",
  190. Path(EXAMPLE_DATA_PATH) + "Cerberus/Cerberus_A.tga",
  191. Path(EXAMPLE_DATA_PATH) + "Cerberus/Cerberus_N.tga",
  192. Path(EXAMPLE_DATA_PATH) + "Cerberus/Cerberus_R.tga",
  193. Path(EXAMPLE_DATA_PATH) + "Cerberus/Cerberus_M.tga",
  194. Path(EXAMPLE_DATA_PATH) + "Particles/Smoke.png",
  195. Path(EXAMPLE_DATA_PATH) + "Decal/DecalAlbedo.png",
  196. Path(EXAMPLE_DATA_PATH) + "Decal/DecalNormal.png",
  197. };
  198. const Path& srcAssetPath = assetPaths[(UINT32)type];
  199. // Attempt to load the previously processed asset
  200. Path assetPath = srcAssetPath;
  201. assetPath.setExtension(srcAssetPath.getExtension() + ".asset");
  202. HTexture texture = gResources().load<Texture>(assetPath);
  203. if (texture == nullptr) // Texture file doesn't exist, import from the source file.
  204. {
  205. // When importing you may specify optional import options that control how is the asset imported.
  206. SPtr<ImportOptions> textureImportOptions = Importer::instance().createImportOptions(srcAssetPath);
  207. // rtti_is_of_type checks if the import options are of valid type, in case the provided path is pointing to a
  208. // non-texture resource. This is similar to dynamic_cast but uses Banshee internal RTTI system for type checking.
  209. if (rtti_is_of_type<TextureImportOptions>(textureImportOptions))
  210. {
  211. TextureImportOptions* importOptions = static_cast<TextureImportOptions*>(textureImportOptions.get());
  212. // We want maximum number of mipmaps to be generated
  213. importOptions->setGenerateMipmaps(mips);
  214. // If the texture is in sRGB space the system needs to know about it
  215. importOptions->setSRGB(isSRGB);
  216. // Ensures we can save the texture contents
  217. importOptions->setCPUCached(true);
  218. // Import as cubemap if needed
  219. importOptions->setIsCubemap(isCubemap);
  220. // If importing as cubemap, assume source is a panorama
  221. importOptions->setCubemapSourceType(CubemapSourceType::Cylindrical);
  222. // Importing using a HDR format if requested
  223. if (isHDR)
  224. importOptions->setFormat(PF_RG11B10F);
  225. }
  226. // Import texture with specified import options
  227. texture = gImporter().import<Texture>(srcAssetPath, textureImportOptions);
  228. // Save for later use, so we don't have to import on the next run.
  229. gResources().save(texture, assetPath, true);
  230. // Register with manifest, if one is present. Manifest allows the engine to find the resource even after
  231. // the application was restarted, which is important if resource was referenced in some serialized object.
  232. if(manifest)
  233. manifest->registerResource(texture.getUUID(), assetPath);
  234. }
  235. return texture;
  236. }
  237. /**
  238. * Loads one of the builtin shader assets. If the asset doesn't exist, the shader will be re-imported from the
  239. * source file, and then saved so it can be loaded on the next call to this method.
  240. */
  241. static HShader loadShader(ExampleShader type)
  242. {
  243. // Map from the enum to the actual file path
  244. static Path assetPaths[] =
  245. {
  246. Path(EXAMPLE_DATA_PATH) + "Shaders/CustomVertex.bsl",
  247. Path(EXAMPLE_DATA_PATH) + "Shaders/CustomDeferredSurface.bsl",
  248. Path(EXAMPLE_DATA_PATH) + "Shaders/CustomDeferredLighting.bsl",
  249. Path(EXAMPLE_DATA_PATH) + "Shaders/CustomForward.bsl",
  250. };
  251. const Path& srcAssetPath = assetPaths[(UINT32)type];
  252. // Attempt to load the previously processed asset
  253. Path assetPath = srcAssetPath;
  254. assetPath.setExtension(srcAssetPath.getExtension() + ".asset");
  255. HShader shader = gResources().load<Shader>(assetPath);
  256. if (shader == nullptr) // Shader file doesn't exist, import from the source file.
  257. {
  258. shader = gImporter().import<Shader>(srcAssetPath);
  259. // Save for later use, so we don't have to import on the next run.
  260. gResources().save(shader, assetPath, true);
  261. // Register with manifest, if one is present. Manifest allows the engine to find the resource even after
  262. // the application was restarted, which is important if resource was referenced in some serialized object.
  263. if(manifest)
  264. manifest->registerResource(shader.getUUID(), assetPath);
  265. }
  266. return shader;
  267. }
  268. /**
  269. * Loads one of the builtin font assets. If the asset doesn't exist, the font will be re-imported from the
  270. * source file, and then saved so it can be loaded on the next call to this method.
  271. *
  272. * Use the 'fontSizes' parameter to determine which sizes of this font should be imported. Note this option is only
  273. * relevant when a font is being imported (i.e. when the asset file is missing).
  274. */
  275. static HFont loadFont(ExampleFont type, Vector<UINT32> fontSizes)
  276. {
  277. // Map from the enum to the actual file path
  278. static Path assetPaths[] =
  279. {
  280. Path(EXAMPLE_DATA_PATH) + "GUI/segoeuil.ttf",
  281. Path(EXAMPLE_DATA_PATH) + "GUI/seguisb.ttf",
  282. };
  283. const Path& srcAssetPath = assetPaths[(UINT32)type];
  284. // Attempt to load the previously processed asset
  285. Path assetPath = srcAssetPath;
  286. assetPath.setExtension(srcAssetPath.getExtension() + ".asset");
  287. HFont font = gResources().load<Font>(assetPath);
  288. if (font == nullptr) // Font file doesn't exist, import from the source file.
  289. {
  290. // When importing you may specify optional import options that control how is the asset imported.
  291. SPtr<FontImportOptions> fontImportOptions = FontImportOptions::create();
  292. fontImportOptions->setFontSizes(fontSizes);
  293. font = gImporter().import<Font>(srcAssetPath, fontImportOptions);
  294. // Save for later use, so we don't have to import on the next run.
  295. gResources().save(font, assetPath, true);
  296. // Register with manifest, if one is present. Manifest allows the engine to find the resource even after
  297. // the application was restarted, which is important if resource was referenced in some serialized object.
  298. if(manifest)
  299. {
  300. manifest->registerResource(font.getUUID(), assetPath);
  301. // Font has child resources, which also need to be registered
  302. for (auto& size : fontSizes)
  303. {
  304. SPtr<const FontBitmap> fontData = font->getBitmap(size);
  305. Path texPageOutputPath = Path(EXAMPLE_DATA_PATH) + "GUI/";
  306. UINT32 pageIdx = 0;
  307. for (const auto& tex : fontData->texturePages)
  308. {
  309. String fontName = srcAssetPath.getFilename(false);
  310. texPageOutputPath.setFilename(fontName + "_" + toString(size) + "_texpage_" +
  311. toString(pageIdx) + ".asset");
  312. gResources().save(tex, texPageOutputPath, true);
  313. manifest->registerResource(tex.getUUID(), texPageOutputPath);
  314. pageIdx++;
  315. }
  316. }
  317. }
  318. }
  319. return font;
  320. }
  321. /**
  322. * Loads one of the builtin non-specific assets. If the asset doesn't exist, it will be re-imported from the
  323. * source file, and then saved so it can be loaded on the next call to this method.
  324. */
  325. template<class T>
  326. static ResourceHandle<T> loadResource(ExampleResource type)
  327. {
  328. // Map from the enum to the actual file path
  329. static Path assetPaths[] =
  330. {
  331. Path(EXAMPLE_DATA_PATH) + "Particles/VectorField.fga",
  332. };
  333. const Path& srcAssetPath = assetPaths[(UINT32)type];
  334. // Attempt to load the previously processed asset
  335. Path assetPath = srcAssetPath;
  336. assetPath.setExtension(srcAssetPath.getExtension() + ".asset");
  337. ResourceHandle<T> resource = gResources().load<T>(assetPath);
  338. if (resource == nullptr) // Shader file doesn't exist, import from the source file.
  339. {
  340. resource = gImporter().import<T>(srcAssetPath);
  341. // Save for later use, so we don't have to import on the next run.
  342. gResources().save(resource, assetPath, true);
  343. // Register with manifest, if one is present. Manifest allows the engine to find the resource even after
  344. // the application was restarted, which is important if resource was referenced in some serialized object.
  345. if(manifest)
  346. manifest->registerResource(resource.getUUID(), assetPath);
  347. }
  348. return resource;
  349. }
  350. private:
  351. static SPtr<ResourceManifest> manifest;
  352. };
  353. SPtr<ResourceManifest> ExampleFramework::manifest;
  354. }