Skip to content

Commit

Permalink
[Examples] Refactored asset loading in examples.
Browse files Browse the repository at this point in the history
- Replaced filename resolution via FindResourcePath() with abstraction to load entire asset via ReadAsset().
- Replaced individual uses of stbi_load() with new ImageReader class.
- Started with asset loading for Android.
- Also use different header guard for examples: Renamed "LLGL_*" to "LLGLEXAMPLES_*".

TODO: Android asset reading will need connection to JNI to access 'AAssetManager'.
  • Loading branch information
LukasBanana committed Jul 6, 2024
1 parent 561e6ab commit c24b013
Show file tree
Hide file tree
Showing 17 changed files with 488 additions and 170 deletions.
14 changes: 10 additions & 4 deletions examples/Cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,20 @@ if(APPLE)
else()
find_source_files(FilesExampleBaseMacOS OBJC "${EXAMPLEBASE_PROJECT_DIR}/macOS")
endif()
endif(APPLE)
elseif(LLGL_ANDROID_PLATFORM)
find_source_files(FilesExampleBaseAndroid CXX "${EXAMPLEBASE_PROJECT_DIR}/Android")
endif()

set(FilesExampleBaseAll ${FilesExampleBase})
if(APPLE)
if(LLGL_IOS_PLATFORM)
set(FilesExampleBaseAll ${FilesExampleBaseAll} ${FilesExampleBaseIOS})
list(APPEND FilesExampleBaseAll ${FilesExampleBaseIOS})
else()
set(FilesExampleBaseAll ${FilesExampleBaseAll} ${FilesExampleBaseMacOS})
list(APPEND FilesExampleBaseAll ${FilesExampleBaseMacOS})
endif()
set_source_files_properties("${EXAMPLEBASE_PROJECT_DIR}/ExampleBase.cpp" PROPERTIES COMPILE_FLAGS -xobjective-c++)
elseif(LLGL_ANDROID_PLATFORM)
list(APPEND FilesExampleBaseAll ${FilesExampleBaseAndroid})
endif()

find_project_source_files( FilesExample_Animation "${EXAMPLE_CPP_PROJECTS_DIR}/Animation" )
Expand Down Expand Up @@ -130,7 +134,9 @@ if(APPLE)
else()
source_group("Sources\\macOS" FILES ${FilesExampleBaseMacOS})
endif()
endif(APPLE)
elseif(LLGL_ANDROID_PLATFORM)
source_group("Sources\\Android" FILES ${FilesExampleBaseAndroid})
endif()


# === Include directories ===
Expand Down
56 changes: 56 additions & 0 deletions examples/Cpp/ExampleBase/Android/AppUtils.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* AppUtils.c (Android)
*
* Copyright (c) 2015 Lukas Hermanns. All rights reserved.
* Licensed under the terms of the BSD 3-Clause license (see LICENSE.txt).
*/

#include "AppUtils.h"
#include <errno.h>


static int AndroidFileRead(void* userData, char* buffer, int size)
{
return AAsset_read((AAsset*)userData, buffer, (size_t)size);
}

static int AndroidFileWrite(void* userData, const char* buffer, int size)
{
return EACCES; // dummy - cannot provide access to write to APK
}

static fpos_t AndroidFileSeek(void* userData, fpos_t offset, int whence)
{
return AAsset_seek((AAsset*)userData, offset, whence);
}

static int AndroidFileClose(void* userData)
{
AAsset_close((AAsset*)userData);
return 0;
}

static AAssetManager* g_assetMngr = NULL;

void AndroidSetAssetManager(AAssetManager* assetMngr)
{
g_assetMngr = assetMngr;
}

FILE* AndroidOpenFile(const char* filename, const char* mode)
{
if (g_assetMngr == NULL)
return NULL; // No asset manager provided

if (mode[0] == 'w')
return NULL; // Write access is not supported on APK package

// Open asset via AAssetManager
AAsset* asset = AAssetManager_open(g_assetMngr, filename, AASSET_MODE_STREAMING);
if (asset == NULL)
return NULL; // Failed to open asset

// Open FILE descriptor with custom read/seek functions
return funopen(asset, AndroidFileRead, AndroidFileWrite, AndroidFileSeek, AndroidFileClose);
}

29 changes: 29 additions & 0 deletions examples/Cpp/ExampleBase/Android/AppUtils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* AppUtils.h (Android)
*
* Copyright (c) 2015 Lukas Hermanns. All rights reserved.
* Licensed under the terms of the BSD 3-Clause license (see LICENSE.txt).
*/

#ifndef __USE_BSD
#define __USE_BSD /* Defines funopen() */
#endif

#include <stdio.h>
#include <android/asset_manager.h>

#ifdef __cplusplus
extern "C" {
#endif

// Sets the current asset manager.
void AndroidSetAssetManager(AAssetManager* assetMngr);

// Opens an STD file stream via the AAssetManager.
FILE* AndroidOpenFile(const char* filename, const char* mode);

#define fopen(NAME, MODE) AndroidOpenFile(NAME, MODE)

#ifdef __cplusplus
} // /extern "C"
#endif
26 changes: 15 additions & 11 deletions examples/Cpp/ExampleBase/DDSImageReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <cstring>
#include <type_traits>
#include <algorithm>
#include <LLGL/Log.h>


static const std::uint32_t ddsMagicNumber = 0x20534444; // 'DDS '
Expand Down Expand Up @@ -131,21 +132,22 @@ T ReadValue(std::istream& stream)
return value;
}

void DDSImageReader::LoadFromFile(const std::string& filename)
bool DDSImageReader::LoadFromFile(const std::string& filename)
{
// Open file for reading
const std::string path = FindResourcePath(filename);
std::ifstream file(path, std::ios::binary);
if (!file.good())
throw std::runtime_error("failed to load DDS image from file: " + path);
AssetReader reader = ReadAsset(filename);
if (!reader)
return false;

// Read magic number
if (ReadValue<std::int32_t>(file) != ddsMagicNumber)
throw std::runtime_error("invalid magic number in DDS image: " + path);
if (reader.Read<std::int32_t>() != ddsMagicNumber)
{
LLGL::Log::Errorf("invalid magic number in DDS image: %s\n", filename.c_str());
return false;
}

// Rerad DDS header
DDSHeader header;
ReadValue(file, header);
DDSHeader header = reader.Read<DDSHeader>();

DDSHeaderDX10 headerDX10;
::memset(&headerDX10, 0, sizeof(headerDX10));
Expand Down Expand Up @@ -207,7 +209,7 @@ void DDSImageReader::LoadFromFile(const std::string& filename)
/*else if (IsFourCC("DX10"))
{
// Read header extension
ReadValue(file, headerDX10);
(void)reader.Read<headerDX10>();
hasDX10Header = true;
}*/
else
Expand Down Expand Up @@ -244,7 +246,9 @@ void DDSImageReader::LoadFromFile(const std::string& filename)

data_.resize(bufferSize);

file.read(data_.data(), static_cast<std::streamsize>(data_.size()));
reader.Read(data_.data(), static_cast<std::streamsize>(data_.size()));

return true;
}

LLGL::ImageView DDSImageReader::GetImageView(std::uint32_t mipLevel) const
Expand Down
10 changes: 5 additions & 5 deletions examples/Cpp/ExampleBase/DDSImageReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
* Licensed under the terms of the BSD 3-Clause license (see LICENSE.txt).
*/

#ifndef LLGL_DDS_IMAGE_READER_H
#define LLGL_DDS_IMAGE_READER_H
#ifndef LLGLEXAMPLES_DDS_IMAGE_READER_H
#define LLGLEXAMPLES_DDS_IMAGE_READER_H


#include <LLGL/ImageFlags.h>
Expand All @@ -15,14 +15,14 @@
#include <vector>


// Perlin noise generator class.
// Image reader class to load DXT compressed textures from file.
class DDSImageReader
{

public:

// Loads the specified DDS texture from file.
void LoadFromFile(const std::string& filename);
// Loads the specified DDS image from file.
bool LoadFromFile(const std::string& filename);

// Returns the image view for the sepcified MIP-map that can be passed to RenderSystem::CreateTexture or RenderSystem::WriteTexture.
LLGL::ImageView GetImageView(std::uint32_t mipLevel = 0) const;
Expand Down
67 changes: 26 additions & 41 deletions examples/Cpp/ExampleBase/ExampleBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <ExampleBase.h>
#include <LLGL/Utils/TypeNames.h>
#include <LLGL/Utils/ForRange.h>
#include "ImageReader.h"
#include "FileUtils.h"
#include <stdio.h>

Expand All @@ -26,6 +27,10 @@
#endif
#include <inttypes.h>

#ifdef LLGL_OS_ANDROID
# include "Android/AppUtils.h"
#endif

#define IMMEDIATE_SUBMIT_CMDBUFFER 0


Expand Down Expand Up @@ -483,6 +488,18 @@ ExampleBase::ExampleBase(const LLGL::UTF8String& title)
rendererDesc.flags |= g_Config.flags;
renderer = LLGL::RenderSystem::Load(rendererDesc);

// Fallback to null device if selected renderer cannot be loaded
if (!renderer)
{
LLGL::Log::Errorf("Failed to load \"%s\" module. Falling back to \"Null\" device.\n", rendererDesc.moduleName.c_str());
renderer = LLGL::RenderSystem::Load("Null");
if (!renderer)
{
LLGL::Log::Errorf("Failed to load \"Null\" module. Exiting.\n");
exit(1);
}
}

// Apply device limits (not for GL, because we won't have a valid GL context until we create our first swap chain)
if (renderer->GetRendererID() == LLGL::RendererID::OpenGL)
samples_ = g_Config.samples;
Expand Down Expand Up @@ -514,8 +531,8 @@ ExampleBase::ExampleBase(const LLGL::UTF8String& title)
commandQueue = renderer->GetCommandQueue();

// Print renderer information
const auto& info = renderer->GetRendererInfo();
const auto swapChainRes = swapChain->GetResolution();
const LLGL::RendererInfo& info = renderer->GetRendererInfo();
const LLGL::Extent2D swapChainRes = swapChain->GetResolution();

LLGL::Log::Printf(
"render system:\n"
Expand Down Expand Up @@ -775,40 +792,17 @@ void ExampleBase::ThrowIfFailed(LLGL::PipelineState* pso)

LLGL::Texture* LoadTextureWithRenderer(LLGL::RenderSystem& renderSys, const std::string& filename, long bindFlags, LLGL::Format format)
{
// Get format informationm
const auto formatAttribs = LLGL::GetFormatAttribs(format);

// Load image data from file (using STBI library, see https://github.com/nothings/stb)
int width = 0, height = 0, components = 0;

const std::string path = FindResourcePath(filename);
stbi_uc* imageBuffer = stbi_load(path.c_str(), &width, &height, &components, static_cast<int>(formatAttribs.components));
if (!imageBuffer)
throw std::runtime_error("failed to load texture from file: \"" + path + "\"");

// Initialize source image descriptor to upload image data onto hardware texture
LLGL::ImageView imageView;
ImageReader reader;
if (!reader.LoadFromFile(filename))
{
// Set image color format
imageView.format = formatAttribs.format;

// Set image data type (unsigned char = 8-bit unsigned integer)
imageView.dataType = LLGL::DataType::UInt8;

// Set image buffer source for texture initial data
imageView.data = imageBuffer;

// Set image buffer size
imageView.dataSize = static_cast<std::size_t>(width*height*4);
// Create dummy texture on load failure
return renderSys.CreateTexture(LLGL::Texture2DDesc(format, 1, 1));
}

// Create texture and upload image data onto hardware texture
LLGL::TextureDescriptor texDesc = LLGL::Texture2DDesc(format, width, height, bindFlags);
texDesc.debugName = filename.c_str();
LLGL::Texture* tex = renderSys.CreateTexture(texDesc, &imageView);

// Release image data
stbi_image_free(imageBuffer);
LLGL::ImageView imageView = reader.GetImageView();
LLGL::Texture* tex = renderSys.CreateTexture(reader.GetTextureDesc(), &imageView);

// Show info
LLGL::Log::Printf("loaded texture: %s\n", filename.c_str());
Expand All @@ -818,17 +812,8 @@ LLGL::Texture* LoadTextureWithRenderer(LLGL::RenderSystem& renderSys, const std:

bool SaveTextureWithRenderer(LLGL::RenderSystem& renderSys, LLGL::Texture& texture, const std::string& filename, std::uint32_t mipLevel)
{
#if 0//TESTING

mipLevel = 1;
LLGL::Extent3D texSize{ 150, 256, 1 };

#else

// Get texture dimension
auto texSize = texture.GetMipExtent(mipLevel);

#endif
const LLGL::Extent3D texSize = texture.GetMipExtent(mipLevel);

// Read texture image data
std::vector<LLGL::ColorRGBAub> imageBuffer(texSize.width * texSize.height);
Expand Down
Loading

0 comments on commit c24b013

Please sign in to comment.