Skip to content

Instantly share code, notes, and snippets.

@define-private-public
Last active August 21, 2022 17:45
Show Gist options
  • Save define-private-public/598cfe7ed59f2258a94f8d0c4e4d5bea to your computer and use it in GitHub Desktop.
Save define-private-public/598cfe7ed59f2258a94f8d0c4e4d5bea to your computer and use it in GitHub Desktop.
PSRayTracing Qt GUI Blog Post
# Check if we're building for iOS (and put it into a more friendly variable)
set(IOS FALSE)
if (${CMAKE_SYSTEM_NAME} STREQUAL iOS)
set(IOS TRUE)
endif()
# If we're building for Android or iOS, turn on the UI
set(IS_MOBILE FALSE)
if (ANDROID OR IOS)
set(IS_MOBILE TRUE)
endif()
# Configurable options
option(BUILD_QT_UI "Build the Qt UI as well (requires Qt 6)" ${IS_MOBILE})
readonly property real title_font_size_pt: 18
readonly property real about_font_size_pt: 10
add_custom_target(fix_render_lib_static_linking
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)
add_custom_command(TARGET fix_render_lib_static_linking
PRE_BUILD
COMMAND ln -sF ${CMAKE_BINARY_DIR}/render_library/Debug-${CMAKE_OSX_SYSROOT} ${CMAKE_BINARY_DIR}/render_library/Debug
)
add_dependencies(PSRayTracing_QtUI fix_render_lib_static_linking)
readonly property real _scaling_factor: Screen.devicePixelRatio
readonly property real title_font_size_pt: _scaling_factor * 18
readonly property real about_font_size_pt: _scaling_factor * 10
set_target_properties(PSRayTracing_QtUI PROPERTIES
XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1,2"
)
readonly property real _scaling_factor: Math.min(1.8, Screen.devicePixelRatio)
readonly property real title_font_size_pt: _scaling_factor * 18
readonly property real about_font_size_pt: _scaling_factor * 10
if (APPLE)
# Required (for on Apple devices) to get an app icon
set(APPLE_ASSETS ${CMAKE_CURRENT_SOURCE_DIR}/apple/Assets.xcassets)
target_sources(PSRayTracing_QtUI PRIVATE ${APPLE_ASSETS})
set_source_files_properties(${APPLE_ASSETS} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
set(APPLE_BUNDLE_ID "net.16bpp.psraytracing")
set(APPLE_EXE_NAME "PSRayTracing")
set(APPLE_SHORT_VERSION_STRING "${PROJECT_VERSION}.0") # Apple requires a "major.minor.patch" string (e.g. "1.2.3"), but I'm only doing "major.minor" (e.g. "1.2")
set_target_properties(PSRayTracing_QtUI PROPERTIES
MACOSX_BUNDLE_GUI_IDENTIFIER ${APPLE_BUNDLE_ID}
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
MACOSX_BUNDLE_EXECUTABLE_NAME ${APPLE_EXE_NAME}
MACOSX_BUNDLE_SHORT_VERSION_STRING ${APPLE_SHORT_VERSION_STRING}
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/apple/Info.plist.in
XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1,2" # Required os that we build for both iPhone & iOS. Without, it would only build for iPhone
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER ${APPLE_BUNDLE_ID} # Fixes an XCode warning
XCODE_ATTRIBUTE_EXECUTABLE_NAME ${APPLE_EXE_NAME} # Makes it so that our app name shows up as "PSRayTracing", without the `_QtUI` at the end
XCODE_ATTRIBUTE_ASSETCATALOG_COMPILER_APPICON_NAME "AppIcon" # Required to make sure our AppIcon works
)
# ...
# Figure out the platform & architecture code we need
set(ANDROID_PLATFORM_ARCHITECTURE_CODE "xxx")
if (${ANDROID_ABI} STREQUAL "armeabi-v7a")
set(ANDROID_PLATFORM_ARCHITECTURE_CODE "032")
elseif (${ANDROID_ABI} STREQUAL "arm64-v8a")
set(ANDROID_PLATFORM_ARCHITECTURE_CODE "064")
elseif (${ANDROID_ABI} STREQUAL "x86")
set(ANDROID_PLATFORM_ARCHITECTURE_CODE "132")
elseif (${ANDROID_ABI} STREQUAL "x86_64")
set(ANDROID_PLATFORM_ARCHITECTURE_CODE "164")
endif()
# Slap the above together with a version code (major and minor only)
set(ANDROID_VERSION_CODE "${ANDROID_PLATFORM_ARCHITECTURE_CODE}${PROJECT_VERSION_MAJOR}${PROJECT_VERSION_MINOR}")
set_property(TARGET PSRayTracing_QtUI APPEND PROPERTY QT_ANDROID_VERSION_CODE ${ANDROID_VERSION_CODE})
QSize android_get_device_resolution_in_pixels()
{
// Call our Java/Android code
const QJniObject dim = QJniObject::callStaticObjectMethod(
"net/sixteenbpp/psraytracing/AndroidUtils",
"get_device_resolution_in_pixels",
"(Landroid/content/Context;)Landroid/graphics/Point;",
QNativeInterface::QAndroidApplication::context()
);
// Get the result
return QSize(
dim.getField<jint>("x"),
dim.getField<jint>("y")
);
}
public class AndroidUtils
{
/**
* This is an Android native method we need to call to get the physical screen size
* (in pixels) of our device.
*
* \a context must be the active application context.
*/
public static Point get_device_resolution_in_pixels(Context context)
{
// NOTE: This API is deprecated in android API 30 and above.
// But I don't see any other way of getting the "real size" (easily) in the
// new recommended API
Point dim = new Point();
context.getDisplay().getRealSize(dim);
return dim;
}
}
ScrollView {
RenderSettingsForm { }
}
// State for controlling the widgets' layout
states: [
// For normal screen widths
// Layout goes left to right: settings button, scene label, scene select, status message, render button
State {
name: 'normal_width'
when: !UITheme.is_width_small(root.width);
AnchorChanges { target: scene_label; anchors.left: settings_button.right; }
AnchorChanges { target: scene_select_dropdown; anchors.right: undefined; }
AnchorChanges { target: settings_button; anchors.top: parent.top; }
AnchorChanges { target: render_info_layout; anchors.left: scene_select_dropdown.right; }
AnchorChanges { target: render_button; anchors.top: parent.top; }
PropertyChanges { target: scene_label; anchors.leftMargin: __p.padding; }
PropertyChanges { target: scene_select_dropdown; width: 330; }
},
// When the width is too tiny
// Scene select (and label) goes on top
// Render settings, status message, and render goes on bottom
State {
name: 'small_width'
when: UITheme.is_width_small(root.width);
AnchorChanges { target: scene_label; anchors.left: parent.left; }
AnchorChanges { target: scene_select_dropdown; anchors.right: parent.right; }
AnchorChanges { target: settings_button; anchors.top: scene_select_dropdown.bottom; }
AnchorChanges { target: render_info_layout; anchors.left: settings_button.right; }
AnchorChanges { target: render_button; anchors.top: scene_select_dropdown.bottom; }
PropertyChanges { target: scene_label; anchors.leftMargin: undefined; }
PropertyChanges { target: scene_select_dropdown; width: undefined; }
}
]
ScrollView {
clip: true
ScrollBar.horizontal.interactive: true
ScrollBar.vertical.interactive: true
Flickable {
width: parent.width
height: parent.height
contentWidth: rsf.width
contentHeight: rsf.height
RenderSettingsForm {
id: rsf
}
}
}
Item {
id: root
// The buttons that appear in the upper right
Rectangle {
ColumnLayout {
Button { }
Button { }
}
MouseArea {
// Normally MouseArea would "steal" the click events, so the buttons above wouldn't be clickable
// But if we do this below, we can have the mouse area only catch the hover events (which is what we want)
// and then the click events can be sent to the buttons
propagateComposedEvents: true
onClicked: function(mouse) { mouse.accepted = false; }
onPressed: function(mouse) { mouse.accepted = false; }
onReleased: function(mouse) { mouse.accepted = false; }
}
}
// Gives us the pan controls
Flickable {
// Give us those "pinch to zoom" controls
PinchArea {
// Mouse area (used for scroll wheel zoom, double click/tap) needs to live inside of the
// pinch area so things work nicely
MouseArea { }
}
// We want the target of the PanZoom to be centered, so we need to put it inside of a container Item.
Item {
// The actual image (itself) we pan/zoom upon
Image { }
}
}
}
QSize ios_utils::get_device_resolution_in_pixels()
{
const CGRect dim = [UIScreen mainScreen].nativeBounds;
QSize native_size(
static_cast<int>(dim.size.width),
static_cast<int>(dim.size.height)
);
if (UIDeviceOrientationIsLandscape([UIDevice currentDevice].orientation))
native_size.transpose();
return native_size;
}
/** A descriptor of how a render should be performed */
struct RenderJob
{
uint16_t render_width = 960;
uint16_t render_height = 540;
std::string scene_id = "book2::final_scene";
uint32_t samples_per_pixel = 10;
uint16_t max_depth = 50;
uint16_t num_threads = 1;
std::string seed_str = "ASDF";
bool deep_copy_per_thread = true;
};
/** The result of a render job. */
struct Render
{
std::vector<uint8_t> image_data;
uint16_t render_width = 0;
uint16_t render_height = 0;
uint8_t num_channels = 0;
float render_time_in_seconds = 0.0f;
std::chrono::nanoseconds render_time_in_nanoseconds;
bool was_succesful() const
{ return !image_data.empty(); }
};
// The fuctions to perform a render
std::future<Render> do_render(const RenderJob &job); ///< Will start a render job (if one isn't in progress)
void stop_active_render(); ///< Will stop a render job (if one is in progress)
float render_progress(); ///< Returns the progress of a render [0.0, 1.0]. Returns less than 0 (e.g. -1) if no render is happening
bool render_in_progress(); ///< Checks to see if a render is already in progress (or not)
std::vector<std::string> all_scene_ids(); ///< Retrives all possible scenes that could be rendered
int num_concurrent_threads_supported(); ///< Retrives the maximum number of threads that can ru concurrently
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment