Display rendered image in Android example.

This commit is contained in:
Syoyo Fujita
2021-08-05 22:13:28 +09:00
parent 6bfc6875a0
commit e40e798aaa
7 changed files with 339 additions and 52 deletions

Binary file not shown.

View File

@@ -14,18 +14,33 @@ set(TINYUSDZ_DEP_SOURCES
${PROJECT_SOURCE_DIR}/../../../../../src/external/ryu/ryu/s2f.c
)
# Reuse files from sdlviewer
set(USDVIEW_SOURCES
${PROJECT_SOURCE_DIR}/../../../../../examples/sdlviewer/simple-render.cc
${PROJECT_SOURCE_DIR}/../../../../../examples/common/matrix.cc
${PROJECT_SOURCE_DIR}/../../../../../examples/common/trackball.cc
)
# Build the libhello-oboe library
add_library(hello-tinyusdz SHARED
jni-tinyusdz.cc
render-ctx.cc
${TINYUSDZ_SOURCES}
${TINYUSDZ_DEP_SOURCES}
${USDVIEW_SOURCES}
)
target_link_libraries(hello-tinyusdz jnigraphics android log)
target_include_directories(hello-tinyusdz PRIVATE
${PROJECT_SOURCE_DIR}/../../../../../src/
${PROJECT_SOURCE_DIR}/../../../../../src/external/ryu
# nanort, nanosg, etc
${PROJECT_SOURCE_DIR}/../../../../../examples/common/
# sdlviewer example
${PROJECT_SOURCE_DIR}/../../../../../examples/sdlviewer/
)
# Required to load .usd files from Android asset for demo purpose

View File

@@ -6,8 +6,7 @@
#include "tinyusdz.hh"
// global
tinyusdz::Scene g_scene;
#include "render-ctx.hh"
#ifndef TINYUSDZ_ANDROID_LOAD_FROM_ASSETS
#error "This demo requires to load .usd file from Android Assets"
@@ -16,48 +15,108 @@ tinyusdz::Scene g_scene;
namespace {
// https://stackoverflow.com/questions/41820039/jstringjni-to-stdstringc-with-utf8-characters
std::string jstring2string(JNIEnv *env, jstring jStr) {
if (!jStr)
return "";
const jclass stringClass = env->GetObjectClass(jStr);
const jmethodID getBytes = env->GetMethodID(stringClass, "getBytes", "(Ljava/lang/String;)[B");
const jbyteArray stringJbytes = (jbyteArray) env->CallObjectMethod(jStr, getBytes, env->NewStringUTF("UTF-8"));
size_t length = (size_t) env->GetArrayLength(stringJbytes);
jbyte* pBytes = env->GetByteArrayElements(stringJbytes, NULL);
std::string ret = std::string((char *)pBytes, length);
env->ReleaseByteArrayElements(stringJbytes, pBytes, JNI_ABORT);
env->DeleteLocalRef(stringJbytes);
env->DeleteLocalRef(stringClass);
return ret;
}
}
extern "C" {
JNIEXPORT jint JNICALL
Java_com_example_hellotinyusdz_MainActivity_updateImage(JNIEnv *env, jobject obj, jintArray _intarray, jint width, jint height) {
JNIEXPORT jint JNICALL
Java_com_example_hellotinyusdz_MainActivity_grabImage(JNIEnv *env, jobject obj, jintArray _intarray, jint width, jint height) {
__android_log_print(ANDROID_LOG_INFO, "tinyusdz", "grabImage");
jint *ptr = env->GetIntArrayElements(_intarray, NULL);
int length = env->GetArrayLength(_intarray);
if (length != (width * height)) {
__android_log_print(ANDROID_LOG_ERROR, "tinyusdz", "Buffer size mismatch");
return -1;
}
uint32_t *buf = reinterpret_cast<uint32_t *>(ptr);
for (size_t y = 0; y < height; y++) {
for (size_t x = 0; x < width; x++) {
uint8_t r = x % 255;
uint8_t g = y % 255;
uint8_t b = 128;
uint8_t a = 255;
// argb
buf[y * width + x] = (a << 24) | (r << 16) | (g << 8) | (b);
}
if (length != (example::g_gui_ctx.aov.width * example::g_gui_ctx.aov.height)) {
__android_log_print(ANDROID_LOG_ERROR, "tinyusdz", "AOV size mismatch");
return -1;
}
uint32_t *dest_ptr = reinterpret_cast<uint32_t *>(ptr);
std::vector<uint32_t> src;
example::GetRenderedImage(example::g_gui_ctx, &src);
if (src.size() != length) {
__android_log_print(ANDROID_LOG_ERROR, "tinyusdz", "GetRenderedImage failed.");
return -1;
}
memcpy(dest_ptr, src.data(), sizeof(uint32_t) * length);
return 1;
}
JNIEXPORT jint JNICALL
Java_com_example_hellotinyusdz_MainActivity_renderImage(
JNIEnv *env,
jobject obj,
jint width, jint height)
{
example::g_gui_ctx.render_width = width;
example::g_gui_ctx.render_height = height;
__android_log_print(ANDROID_LOG_INFO, "tinyusdz", "renderImage");
__android_log_print(ANDROID_LOG_INFO, "tinyusdz", "draw_meshes %d", (int)example::g_gui_ctx.render_scene.draw_meshes.size());
bool ret = example::RenderScene(example::g_gui_ctx);
if (!ret) {
__android_log_print(ANDROID_LOG_ERROR, "tinyusdz", "RenderScene failed.");
}
return 1;
}
/*
* Returns: 0 - success
* -1 - failed
*/
JNIEXPORT jint JNICALL
Java_com_example_hellotinyusdz_MainActivity_createStream(
Java_com_example_hellotinyusdz_MainActivity_initScene(
JNIEnv *env,
jobject obj,
jobject assetManager) {
jobject assetManager,
jstring _filename) {
std::string filename = jstring2string(env, _filename);
tinyusdz::asset_manager = AAssetManager_fromJava(env, assetManager);
tinyusdz::USDLoadOptions options;
// load from Android asset folder
example::g_gui_ctx.scene = tinyusdz::Scene(); // reset
std::string warn, err;
bool ret = LoadUSDCFromFile("suzanne.usdc", &g_scene, &warn, &err, options);
bool ret = LoadUSDCFromFile(filename, &example::g_gui_ctx.scene, &warn, &err, options);
if (warn.size()) {
__android_log_print(ANDROID_LOG_WARN, "tinyusdz", "USD load warning: %s", warn.c_str());
@@ -70,11 +129,16 @@ extern "C" {
}
if (ret) {
__android_log_print(ANDROID_LOG_INFO, "tinyusdz", "USD loaded. #of geom_meshes: %d", int(g_scene.geom_meshes.size()));
return int(g_scene.geom_meshes.size());
__android_log_print(ANDROID_LOG_INFO, "tinyusdz", "USD loaded. #of geom_meshes: %d", int(example::g_gui_ctx.scene.geom_meshes.size()));
}
// err
return -1;
ret = example::SetupScene(example::g_gui_ctx);
if (!ret) {
__android_log_print(ANDROID_LOG_ERROR, "tinyusdz", "SetupScene failed");
return -1;
}
return int(example::g_gui_ctx.scene.geom_meshes.size()); // OK
}
}

View File

@@ -0,0 +1,125 @@
// SPDX-License-Identifier: MIT
#include <android/log.h>
#include "render-ctx.hh"
namespace example {
GUIContext g_gui_ctx;
namespace {
// TODO: Use pow table for faster conversion.
inline float linearToSrgb(float x) {
if (x <= 0.0f)
return 0.0f;
else if (x >= 1.0f)
return 1.0f;
else if (x < 0.0031308f)
return x * 12.92f;
else
return std::pow(x, 1.0f / 2.4f) * 1.055f - 0.055f;
}
inline uint8_t ftouc(float f) {
int val = int(f * 255.0f);
val = std::max(0, std::min(255, val));
return static_cast<uint8_t>(val);
}
inline double radians(double degree) { return 3.141592653589 * degree / 180.0; }
// https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles
std::array<double, 4> ToQuaternion(double yaw, double pitch,
double roll) // yaw (Z), pitch (Y), roll (X)
{
// Abbreviations for the various angular functions
double cy = std::cos(yaw * 0.5);
double sy = std::sin(yaw * 0.5);
double cp = std::cos(pitch * 0.5);
double sp = std::sin(pitch * 0.5);
double cr = std::cos(roll * 0.5);
double sr = std::sin(roll * 0.5);
std::array<double, 4> q;
q[0] = cr * cp * cy + sr * sp * sy;
q[1] = sr * cp * cy - cr * sp * sy;
q[2] = cr * sp * cy + sr * cp * sy;
q[3] = cr * cp * sy - sr * sp * cy;
return q;
}
} // namespace
bool SetupScene(GUIContext &ctx) {
__android_log_print(ANDROID_LOG_INFO, "tinyusdz", "SetupScene");
if (ctx.scene.geom_meshes.empty()) {
__android_log_print(ANDROID_LOG_ERROR, "tinyusdz", "No GeomMesh");
// No GeomMesh in the scene
return false;
}
// Convert USD geom_mesh to renderable mesh.
ctx.render_scene.draw_meshes.clear();
__android_log_print(ANDROID_LOG_INFO, "tinyusdz", "# of geom_meshes %d", (int)ctx.scene.geom_meshes.size());
for (size_t i = 0; i < ctx.scene.geom_meshes.size(); i++) {
example::DrawGeomMesh draw_mesh(&ctx.scene.geom_meshes[i]);
ctx.render_scene.draw_meshes.push_back(draw_mesh);
}
// Setup render mesh
if (!ctx.render_scene.Setup()) {
__android_log_print(ANDROID_LOG_ERROR, "tinyusdz", "Scene::Setup failed");
return false;
}
// init camera matrix
{
auto q = ToQuaternion(radians(ctx.yaw), radians(ctx.pitch),
radians(ctx.roll));
ctx.camera.quat[0] = q[0];
ctx.camera.quat[1] = q[1];
ctx.camera.quat[2] = q[2];
ctx.camera.quat[3] = q[3];
}
// HACK. camera position adjusted for `suzanne.usdc`
ctx.camera.eye[2] = 3.5f;
return true;
}
bool RenderScene(GUIContext &ctx)
{
// Init AOV image
ctx.aov.Resize(ctx.render_width, ctx.render_height);
example::Render(ctx.render_scene, ctx.camera, &ctx.aov);
return true;
}
void GetRenderedImage(const GUIContext &ctx, std::vector<uint32_t> *argb) {
if (!argb) {
return;
}
std::vector<uint32_t> buf;
buf.resize(ctx.aov.width * ctx.aov.height);
for (size_t i = 0; i < ctx.aov.width * ctx.aov.height; i++) {
uint8_t r = ftouc(linearToSrgb(ctx.aov.rgb[3 * i + 0]));
uint8_t g = ftouc(linearToSrgb(ctx.aov.rgb[3 * i + 1]));
uint8_t b = ftouc(linearToSrgb(ctx.aov.rgb[3 * i + 2]));
uint8_t a = 255;
buf[i] = (a << 24) | (r << 16) | (g << 8) | b;
}
(*argb) = buf;
}
} // example

View File

@@ -0,0 +1,82 @@
// SPDX-License-Identifier: MIT
#pragma once
#include "simple-render.hh"
namespace example {
struct GUIContext {
enum AOVMode {
AOV_COLOR = 0,
AOV_SHADING_NORMAL,
AOV_GEOMETRIC_NORMAL,
AOV_POSITION,
AOV_DEPTH,
AOV_TEXCOORD,
AOV_VARYCOORD,
AOV_VERTEXCOLOR
};
int aov_mode{AOV_COLOR};
example::AOV aov; // framebuffer
int width = 1024;
int height = 768;
int mouse_x = -1;
int mouse_y = -1;
bool mouse_left_down = false;
bool shift_pressed = false;
bool ctrl_pressed = false;
bool tab_pressed = false;
float yaw = 90.0f; // for Z up scene
float pitch = 0.0f;
float roll = 0.0f;
// float curr_quat[4] = {0.0f, 0.0f, 0.0f, 1.0f};
// std::array<float, 4> prev_quat[4] = {0.0f, 0.0f, 0.0f, 1.0f};
// std::array<float, 3> eye = {0.0f, 0.0f, 5.0f};
// std::array<float, 3> lookat = {0.0f, 0.0f, 0.0f};
// std::array<float, 3> up = {0.0f, 1.0f, 0.0f};
example::RenderScene render_scene;
example::Camera camera;
std::atomic<bool> update_texture{false};
std::atomic<bool> redraw{true}; // require redraw
std::atomic<bool> quit{false};
int render_width = 512;
int render_height = 512;
// scene reload
tinyusdz::Scene scene;
std::atomic<bool> request_reload{false};
std::string filename;
#if __EMSCRIPTEN__ || defined(EMULATE_EMSCRIPTEN)
bool render_finished{false};
int current_render_line = 0;
int render_line_size = 32; // render images with this lines per animation loop.
// for emscripten environment
#endif
};
extern GUIContext g_gui_ctx;
// Setup scene. Only need to call once USD scene(ctx.scene) is loaded.
bool SetupScene(GUIContext &ctx);
// Render the scene(and store rendered image to ctx.aov)
bool RenderScene(GUIContext &ctx);
// Get rendered image from ctx.aov as Android ARGB_8888 IntArray.
void GetRenderedImage(const GUIContext &ctx, std::vector<uint32_t> *argb);
} // example

View File

@@ -31,34 +31,31 @@ import kotlinx.android.synthetic.main.activity_main.sample_text
class MainActivity : AppCompatActivity() {
val render_width = 512;
val render_height = 512;
fun updateRender() {
renderImage(render_width, render_height);
var conf = Bitmap.Config.ARGB_8888
var b = Bitmap.createBitmap(render_width, render_height, conf)
var pixels = IntArray(render_width * render_height)
grabImage(pixels, render_width, render_height)
b.setPixels(pixels, 0, render_width, 0, 0, render_width, render_height)
var img = findViewById<ImageView>(R.id.imageView)
img.setImageBitmap(b)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val view = findViewById<View>(R.id.container)
var conf = Bitmap.Config.ARGB_8888
var b = Bitmap.createBitmap(512, 512, conf)
var pixels = IntArray(512 * 512)
//b.getPixels(pixels, 0,512, 0, 0, 512, 512)
//for (y in 0 until 512) {
// for (x in 0 until 512) {
// pixels[y * 512 + x] = Color.argb(125, x % 256, y % 256, 64)
// }
//}
var width = 512
var height = 512
updateImage(pixels, width, height)
b.setPixels(pixels, 0, 512, 0, 0, 512, 512)
var img = findViewById<ImageView>(R.id.imageView)
img.setImageBitmap(b)
// Set up a touch listener which calls the native sound engine
view.setOnTouchListener {_, event ->
@@ -76,7 +73,7 @@ class MainActivity : AppCompatActivity() {
override fun onResume() {
super.onResume()
var n = createStream(getAssets());
var n = initScene(getAssets(), "suzanne.usdc");
if (n <= 0) {
val errorString : String = "Failed to load USD file"
@@ -87,6 +84,7 @@ class MainActivity : AppCompatActivity() {
Toast.makeText(applicationContext, s,Toast.LENGTH_LONG).show()
sample_text.text = s
updateRender()
}
}
@@ -95,9 +93,9 @@ class MainActivity : AppCompatActivity() {
}
// Creates and starts Oboe stream to play audio
private external fun createStream(mgr: AssetManager) : Int
private external fun updateImage(img: IntArray, width: Int, height: Int) : Int
private external fun initScene(mgr: AssetManager, filename: String) : Int
private external fun renderImage(width: Int, height: Int) : Int
private external fun grabImage(img: IntArray, width: Int, height: Int) : Int
companion object {
// Used to load native code calling oboe on app startup.

View File

@@ -346,7 +346,7 @@ bool Render(const RenderScene& scene, const Camera& cam, AOV* output) {
// auto startT = std::chrono::system_clock::now();
for (auto t = 0; t < num_threads; t++) {
workers.emplace_back(std::thread([&, t]() {
workers.emplace_back(std::thread([&]() {
int y = 0;
while ((y = i++) < height) {
for (int x = 0; x < width; x++) {
@@ -371,8 +371,13 @@ bool Render(const RenderScene& scene, const Camera& cam, AOV* output) {
bool hit = false;
output->rgb[3 * pixel_idx + 0] = 0.0f;
output->rgb[3 * pixel_idx + 1] = 0.0f;
output->rgb[3 * pixel_idx + 2] = 0.0f;
if (scene.draw_meshes.size()) {
// HACK. Use the first mesh
const DrawGeomMesh& mesh = scene.draw_meshes[0];
@@ -472,13 +477,11 @@ bool Render(const RenderScene& scene, const Camera& cam, AOV* output) {
output->texcoords[2 * pixel_idx + 0] = texcoord[0];
output->texcoords[2 * pixel_idx + 1] = texcoord[1];
}
} else {
}
if (!hit) {
output->rgb[3 * pixel_idx + 0] = 0.0f;
output->rgb[3 * pixel_idx + 1] = 0.0f;
output->rgb[3 * pixel_idx + 2] = 0.0f;
output->geometric_normal[3 * pixel_idx + 0] = 0.0f;
output->geometric_normal[3 * pixel_idx + 1] = 0.0f;