/************************************************************************* * Copyright (C) 2010-2011 Tavian Barnes * * * * This file is part of The Dimension Android Binding. * * * * The Dimension Android Binding is free software; you can redistribute * * it and/or modify it under the terms of the GNU General Public License * * as published by the Free Software Foundation; either version 3 of the * * License, or (at your option) any later version. * * * * The Dimension Android Binding is distributed in the hope that it will * * be useful, but WITHOUT ANY WARRANTY; without even the implied * * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * * the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * *************************************************************************/ /** * @file * Java interface to libdimension used by Android. */ #include "dimension-internal.h" #include #include #include #include #define TAG "libdimension_jni" static void dmnsn_test_scene_set_defaults(dmnsn_scene *scene) { /* Default texture */ scene->default_texture->pigment = dmnsn_new_solid_pigment(DMNSN_TCOLOR(dmnsn_black)); dmnsn_finish *default_finish = &scene->default_texture->finish; default_finish->ambient = dmnsn_new_ambient( dmnsn_color_from_sRGB(dmnsn_color_mul(0.1, dmnsn_white)) ); default_finish->diffuse = dmnsn_new_lambertian(dmnsn_sRGB_inverse_gamma(0.7)); } static void dmnsn_test_scene_add_canvas(dmnsn_scene *scene, size_t width, size_t height) { scene->canvas = dmnsn_new_canvas(width, height); } static void dmnsn_test_scene_add_camera(dmnsn_scene *scene) { /* Set up the transformation matrix for the perspective camera */ dmnsn_matrix trans = dmnsn_scale_matrix( dmnsn_new_vector( ((double)scene->canvas->width)/scene->canvas->height, 1.0, 1.0 ) ); trans = dmnsn_matrix_mul( dmnsn_rotation_matrix(dmnsn_new_vector(0.0624188099959577, 0.0, 0.0)), trans ); trans = dmnsn_matrix_mul( dmnsn_translation_matrix(dmnsn_new_vector(0.0, 0.25, -4.0)), trans ); trans = dmnsn_matrix_mul( dmnsn_rotation_matrix(dmnsn_new_vector(0.0, dmnsn_radians(53.0), 0.0)), trans ); /* Create a perspective camera */ scene->camera = dmnsn_new_perspective_camera(); scene->camera->trans = trans; } static void dmnsn_test_scene_add_background(dmnsn_scene *scene) { scene->background = dmnsn_new_solid_pigment(dmnsn_clear); } static void dmnsn_test_scene_add_lights(dmnsn_scene *scene) { dmnsn_light *light = dmnsn_new_point_light( dmnsn_new_vector(-15.0, 20.0, 10.0), dmnsn_white ); dmnsn_array_push(scene->lights, &light); } static void dmnsn_test_scene_add_hollow_cube(dmnsn_scene *scene) { dmnsn_object *cube = dmnsn_new_cube(); cube->trans = dmnsn_rotation_matrix( dmnsn_new_vector(dmnsn_radians(45.0), 0.0, 0.0) ); cube->texture = dmnsn_new_texture(); dmnsn_tcolor cube_color = dmnsn_new_tcolor(dmnsn_blue, 0.75, 1.0/3.0); cube->texture->pigment = dmnsn_new_solid_pigment(cube_color); dmnsn_color reflect = dmnsn_color_from_sRGB(dmnsn_color_mul(0.5, dmnsn_white)); cube->texture->finish.reflection = dmnsn_new_basic_reflection(dmnsn_black, reflect, 1.0); cube->interior = dmnsn_new_interior(); cube->interior->ior = 1.1; dmnsn_object *sphere = dmnsn_new_sphere(); sphere->texture = dmnsn_new_texture(); sphere->texture->pigment = dmnsn_new_solid_pigment(DMNSN_TCOLOR(dmnsn_green)); sphere->texture->finish.specular = dmnsn_new_phong(dmnsn_sRGB_inverse_gamma(0.2), 40.0); sphere->trans = dmnsn_scale_matrix(dmnsn_new_vector(1.25, 1.25, 1.25)); dmnsn_object *hollow_cube = dmnsn_new_csg_difference(cube, sphere); dmnsn_array_push(scene->objects, &hollow_cube); } #define dmnsn_pigment_map_add_color(map, n, color) \ do { \ dmnsn_tcolor tcolor = DMNSN_TCOLOR(color); \ dmnsn_pigment *pigment = dmnsn_new_solid_pigment(tcolor); \ dmnsn_map_add_entry(map, n, &pigment); \ } while (0) static void dmnsn_test_scene_add_spike(dmnsn_scene *scene) { dmnsn_array *arrow_array = dmnsn_new_array(sizeof(dmnsn_object *)); dmnsn_object *cylinder = dmnsn_new_cone(0.1, 0.1, false); cylinder->trans = dmnsn_scale_matrix(dmnsn_new_vector(1.0, 1.25, 1.0)); dmnsn_array_push(arrow_array, &cylinder); dmnsn_object *cone = dmnsn_new_cone(0.1, 0.0, true); cone->trans = dmnsn_matrix_mul( dmnsn_translation_matrix(dmnsn_new_vector(0.0, 1.375, 0.0)), dmnsn_scale_matrix(dmnsn_new_vector(1.0, 0.125, 1.0)) ); dmnsn_array_push(arrow_array, &cone); dmnsn_object *arrow = dmnsn_new_csg_union(arrow_array); dmnsn_delete_array(arrow_array); dmnsn_pattern *gradient = dmnsn_new_gradient_pattern(dmnsn_y); dmnsn_map *gradient_pigment_map = dmnsn_new_pigment_map(); dmnsn_pigment_map_add_color(gradient_pigment_map, 0.0, dmnsn_red); dmnsn_pigment_map_add_color(gradient_pigment_map, 1.0/6.0, dmnsn_orange); dmnsn_pigment_map_add_color(gradient_pigment_map, 2.0/6.0, dmnsn_yellow); dmnsn_pigment_map_add_color(gradient_pigment_map, 3.0/6.0, dmnsn_green); dmnsn_pigment_map_add_color(gradient_pigment_map, 4.0/6.0, dmnsn_blue); dmnsn_pigment_map_add_color(gradient_pigment_map, 5.0/6.0, dmnsn_magenta); dmnsn_pigment_map_add_color(gradient_pigment_map, 1.0, dmnsn_red); arrow->texture = dmnsn_new_texture(); arrow->texture->pigment = dmnsn_new_pigment_map_pigment(gradient, gradient_pigment_map, DMNSN_PIGMENT_MAP_SRGB); arrow->texture->trans = dmnsn_matrix_mul( dmnsn_translation_matrix(dmnsn_new_vector(0.0, -1.25, 0.0)), dmnsn_scale_matrix(dmnsn_new_vector(1.0, 2.75, 1.0)) ); dmnsn_array *torus_array = dmnsn_new_array(sizeof(dmnsn_object *)); dmnsn_object *torus1 = dmnsn_new_torus(0.15, 0.05); torus1->trans = dmnsn_translation_matrix(dmnsn_new_vector(0.0, -1.0, 0.0)); dmnsn_array_push(torus_array, &torus1); dmnsn_object *torus2 = dmnsn_new_torus(0.15, 0.05); dmnsn_array_push(torus_array, &torus2); dmnsn_object *torus3 = dmnsn_new_torus(0.15, 0.05); torus3->trans = dmnsn_translation_matrix(dmnsn_new_vector(0.0, 1.0, 0.0)); dmnsn_array_push(torus_array, &torus3); dmnsn_object *torii = dmnsn_new_csg_union(torus_array); dmnsn_delete_array(torus_array); torii->texture = dmnsn_new_texture(); torii->texture->pigment = dmnsn_new_solid_pigment(DMNSN_TCOLOR(dmnsn_blue)); torii->texture->finish.ambient = dmnsn_new_ambient(dmnsn_white); dmnsn_array *spike_array = dmnsn_new_array(sizeof(dmnsn_object *)); dmnsn_array_push(spike_array, &arrow); dmnsn_array_push(spike_array, &torii); dmnsn_object *spike = dmnsn_new_csg_union(spike_array); dmnsn_delete_array(spike_array); spike->trans = dmnsn_rotation_matrix( dmnsn_new_vector(dmnsn_radians(-45.0), 0.0, 0.0) ); dmnsn_array_push(scene->objects, &spike); } static void dmnsn_test_scene_add_triangle_strip(dmnsn_scene *scene) { dmnsn_array *strip_array = dmnsn_new_array(sizeof(dmnsn_object *)); dmnsn_vector a = dmnsn_zero; dmnsn_vector b = dmnsn_new_vector(0.0, sqrt(3.0)/2.0, 0.5); dmnsn_vector c = dmnsn_z; dmnsn_texture *strip_textures[3] = { dmnsn_new_texture(), dmnsn_new_texture(), dmnsn_new_texture(), }; strip_textures[0]->pigment = dmnsn_new_solid_pigment(DMNSN_TCOLOR(dmnsn_red)); strip_textures[1]->pigment = dmnsn_new_solid_pigment(DMNSN_TCOLOR(dmnsn_orange)); strip_textures[2]->pigment = dmnsn_new_solid_pigment(DMNSN_TCOLOR(dmnsn_yellow)); for (unsigned int i = 0; i < 128; ++i) { dmnsn_object *triangle = dmnsn_new_flat_triangle(a, b, c); triangle->texture = strip_textures[i%3]; DMNSN_INCREF(triangle->texture); dmnsn_array_push(strip_array, &triangle); a = b; b = c; c = dmnsn_vector_add(a, dmnsn_z); } for (unsigned int i = 0; i < 3; ++i) { dmnsn_delete_texture(strip_textures[i]); } dmnsn_object *strip = dmnsn_new_csg_union(strip_array); dmnsn_delete_array(strip_array); strip->trans = dmnsn_translation_matrix(dmnsn_new_vector(5.0, -2.0, -4.0)); dmnsn_array_push(scene->objects, &strip); } static void dmnsn_test_scene_add_ground(dmnsn_scene *scene) { dmnsn_object *plane = dmnsn_new_plane(dmnsn_new_vector(0.0, 1.0, 0.0)); plane->trans = dmnsn_translation_matrix(dmnsn_new_vector(0.0, -2.0, 0.0)); dmnsn_pattern *checker1 = dmnsn_new_checker_pattern(); dmnsn_pattern *checker2 = dmnsn_new_checker_pattern(); dmnsn_map *small_map = dmnsn_new_pigment_map(); dmnsn_pigment_map_add_color(small_map, 0.0, dmnsn_black); dmnsn_pigment_map_add_color(small_map, 1.0, dmnsn_white); dmnsn_pigment *small_pigment = dmnsn_new_pigment_map_pigment(checker1, small_map, DMNSN_PIGMENT_MAP_REGULAR); small_pigment->trans = dmnsn_scale_matrix(dmnsn_new_vector(1.0/3.0, 1.0/3.0, 1.0/3.0)); dmnsn_map *big_map = dmnsn_new_pigment_map(); dmnsn_pigment_map_add_color(big_map, 0.0, dmnsn_white); dmnsn_map_add_entry(big_map, 1.0, &small_pigment); plane->texture = dmnsn_new_texture(); plane->texture->pigment = dmnsn_new_pigment_map_pigment(checker2, big_map, DMNSN_PIGMENT_MAP_REGULAR); plane->texture->pigment->quick_color = DMNSN_TCOLOR( dmnsn_color_from_sRGB( dmnsn_new_color(1.0, 0.5, 0.75) ) ); dmnsn_array_push(scene->objects, &plane); } static void dmnsn_test_scene_add_objects(dmnsn_scene *scene) { dmnsn_test_scene_add_hollow_cube(scene); dmnsn_test_scene_add_spike(scene); dmnsn_test_scene_add_triangle_strip(scene); dmnsn_test_scene_add_ground(scene); } /* * Test scene */ static dmnsn_scene * dmnsn_new_test_scene(size_t width, size_t height) { dmnsn_scene *scene = dmnsn_new_scene(); dmnsn_test_scene_set_defaults(scene); dmnsn_test_scene_add_canvas(scene, width, height); dmnsn_test_scene_add_camera(scene); dmnsn_test_scene_add_background(scene); dmnsn_test_scene_add_lights(scene); dmnsn_test_scene_add_objects(scene); return scene; } static JavaVM *jvm; static jobject shared_obj; static jclass shared_cls; static jmethodID method; static dmnsn_scene *scene; static dmnsn_future *future; static pthread_key_t dmnsn_detach; static pthread_once_t dmnsn_detach_once = PTHREAD_ONCE_INIT; static void dmnsn_jni_optimizer_fn(const dmnsn_canvas *canvas, dmnsn_canvas_optimizer optimizer, size_t x, size_t y); JNIEXPORT void JNICALL Java_com_tavianator_dimension_android_RaytracedView_renderNative(JNIEnv *env, jobject obj, jint width, jint height) { scene = dmnsn_new_test_scene(width, height); (*env)->GetJavaVM(env, &jvm); shared_obj = (*env)->NewGlobalRef(env, obj); jclass cls = (*env)->GetObjectClass(env, shared_obj); shared_cls = (*env)->NewGlobalRef(env, cls); method = (*env)->GetMethodID(env, shared_cls, "setPixel", "(III)V"); /* Use a canvas optimizer to pass pixels back to the JVM */ dmnsn_canvas_optimizer optimizer; optimizer.optimizer_fn = dmnsn_jni_optimizer_fn; optimizer.free_fn = NULL; dmnsn_canvas_optimize(scene->canvas, optimizer); __android_log_print(ANDROID_LOG_INFO, TAG, "Rendering with %u threads ...", scene->nthreads); future = dmnsn_ray_trace_async(scene); } JNIEXPORT void JNICALL Java_com_tavianator_dimension_android_RaytracedView_waitNative(JNIEnv *env, jobject obj, jdouble progress) { dmnsn_future_wait(future, progress); } JNIEXPORT void JNICALL Java_com_tavianator_dimension_android_RaytracedView_joinNative(JNIEnv *env, jobject obj) { dmnsn_future_join(future); __android_log_print(ANDROID_LOG_INFO, TAG, "Rendered!"); dmnsn_delete_scene(scene); (*env)->DeleteGlobalRef(env, shared_cls); (*env)->DeleteGlobalRef(env, shared_obj); } static void dmnsn_do_detach(void *ptr) { (*jvm)->DetachCurrentThread(jvm); } static void dmnsn_initialize_detach(void) { dmnsn_key_create(&dmnsn_detach, dmnsn_do_detach); } static void dmnsn_jni_optimizer_fn(const dmnsn_canvas *canvas, dmnsn_canvas_optimizer optimizer, size_t x, size_t y) { dmnsn_tcolor tcolor = dmnsn_canvas_get_pixel(canvas, x, y); tcolor = dmnsn_tcolor_remove_filter(tcolor); tcolor.c = dmnsn_color_to_sRGB(tcolor.c); tcolor = dmnsn_tcolor_saturate(tcolor); uint32_t argb = 0; unsigned char component = (unsigned char) ((1.0 - tcolor.T)*UCHAR_MAX); argb |= component << UINT32_C(24); component = (unsigned char) (tcolor.c.R*UCHAR_MAX); argb |= component << UINT32_C(16); component = (unsigned char) (tcolor.c.G*UCHAR_MAX); argb |= component << UINT32_C(8); component = (unsigned char) (tcolor.c.B*UCHAR_MAX); argb |= component; dmnsn_once(&dmnsn_detach_once, dmnsn_initialize_detach); JNIEnv *env = pthread_getspecific(dmnsn_detach); if (env == NULL) { (*jvm)->AttachCurrentThread(jvm, &env, NULL); pthread_setspecific(dmnsn_detach, env); } (*env)->CallVoidMethod(env, shared_obj, method, (jint) x, (jint) y, (jint) argb); }