Skip to content

Commit

Permalink
Merge branch 'main' into Add-Media-Reference-to-inspector
Browse files Browse the repository at this point in the history
  • Loading branch information
jspada200 authored Oct 1, 2024
2 parents 49cd938 + e8cda49 commit bfc40d6
Show file tree
Hide file tree
Showing 12 changed files with 261 additions and 25 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@


build
.vscode

.vscode
*.DS_Store
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ elseif(EMSCRIPTEN)
target_sources(raven PUBLIC main_emscripten.cpp)
set(LIBS ${CMAKE_DL_LIBS} SDL2)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s USE_SDL=2 -s DISABLE_EXCEPTION_CATCHING=1")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s WASM=1 -s NO_EXIT_RUNTIME=0 -s ASSERTIONS=1 -s NO_FILESYSTEM=1 -s USE_PTHREADS=1 -s ALLOW_MEMORY_GROWTH=1 -Wl,--shared-memory,--no-check-features --shell-file ${CMAKE_CURRENT_LIST_DIR}/shell_minimal.html")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s WASM=1 -s NO_EXIT_RUNTIME=0 -s ASSERTIONS=1 -s FETCH -s USE_PTHREADS=1 -s ALLOW_MEMORY_GROWTH=1 -s EXPORTED_RUNTIME_METHODS=ccall,cwrap -Wl,--shared-memory,--no-check-features --shell-file ${CMAKE_CURRENT_LIST_DIR}/shell_minimal.html")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DIMGUI_DISABLE_FILE_FUNCTIONS")
set_target_properties(raven PROPERTIES SUFFIX .html)
target_link_libraries(raven PUBLIC ${LIBS})
Expand Down
37 changes: 27 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,27 +26,44 @@ Linux (Ubuntu, or similar):
- A recent version of CMake
- You can get this via `sudo snap install cmake` or by downloading from https://cmake.org/download/

__Note__: Before building, please ensure that you clone this project with the `--recursive` flag.
This will also clone and initialize all of the submodules that this project depends on.

## Building (macOS, Windows, Linux)

% mkdir build
% cd build
% cmake ..
% cmake --build . -j
% ./raven ../example.otio
Spin up your favourite terminal and follow these steps:

```shell
git submodule update --init --recursive
mkdir build
cd build
cmake ..
cmake --build . -j
./raven ../example.otio
```

## Building (WASM via Emscripten)

You will need to install the [Emscripten toolchain](https://emscripten.org) first.

% mkdir build-web
% cd build-web
% emcmake cmake ..
% cmake --build .
% emrun ./raven.html
```shell
git submodule update --init --recursive
mkdir build-web
cd build-web
emcmake cmake ..
cmake --build .
emrun ./raven.html
```

See also: `serve.py` as an alternative to `emrun`, and as
a reference for which HTTP headers are needed to host the WASM build.

You can load a file into WASM Raven a few ways:
- Add a JSON string to Module.otioLoadString in the HTML file
- Add a URL to Module.otioLoadURL in the HTML file
- Call Module.LoadString(otio_data) at runtime
- Call Module.LoadURL(otio_url) at runtime

Note: The WASM build of raven is missing some features - see the Help Wanted section below.

## Troubleshooting
Expand Down
116 changes: 115 additions & 1 deletion app.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

void DrawMenu();
void DrawToolbar(ImVec2 buttonSize);
void DrawDroppedFilesPrompt();

#define DEFINE_APP_THEME_NAMES
#include "app.h"
Expand All @@ -37,6 +38,9 @@ AppTheme appTheme;

ImFont* gFont = nullptr;

// Variable to store dropped file to load
std::string prompt_dropped_file = "";

// Log a message to the terminal
void Log(const char* format, ...) {
va_list args;
Expand Down Expand Up @@ -241,6 +245,32 @@ void LoadTimeline(otio::Timeline* timeline) {
SelectObject(timeline);
}

void LoadString(std::string json) {
auto start = std::chrono::high_resolution_clock::now();

otio::ErrorStatus error_status;
auto timeline = dynamic_cast<otio::Timeline*>(
otio::Timeline::from_json_string(json, &error_status));
if (!timeline || otio::is_error(error_status)) {
Message(
"Error loading JSON: %s",
otio_error_string(error_status).c_str());
return;
}

LoadTimeline(timeline);

appState.file_path = timeline->name().c_str();

auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsed = (end - start);
double elapsed_seconds = elapsed.count();
Message(
"Loaded \"%s\" in %.3f seconds",
timeline->name().c_str(),
elapsed_seconds);
}

void LoadFile(std::string path) {
auto start = std::chrono::high_resolution_clock::now();

Expand Down Expand Up @@ -315,6 +345,43 @@ void MainInit(int argc, char** argv, int initial_width, int initial_height) {

void MainCleanup() { }

// Validate that a file has the .otio extension
bool is_valid_file(const std::string& filepath) {
size_t last_dot = filepath.find_last_of('.');

// If no dot is found, it's not a valid file
if (last_dot == std::string::npos) {
return false;
}

// Get and check the extension
std::string extension = filepath.substr(last_dot + 1);
return extension == "otio";
}

// Accept and open a file path
void FileDropCallback(int count, const char** filepaths) {
if (count > 1){
Message("Cannot open multiple files.");
return;
}

else if (count == 0) {
return;
}

std::string file_path = filepaths[0];

if (!is_valid_file(file_path)){
Message("Invalid file: %s", file_path.c_str());
return;
}

// Loading is done in DrawDroppedFilesPrompt()
prompt_dropped_file = file_path;

}

// Make a button using the fancy icon font
bool IconButton(const char* label, const ImVec2 size = ImVec2(0, 0)) {
bool result = ImGui::Button(label, size);
Expand Down Expand Up @@ -383,6 +450,7 @@ void MainGui() {
exit(0);
}

DrawDroppedFilesPrompt();
DrawMenu();

// ImGui::SameLine(ImGui::GetContentRegionAvailWidth() - button_size.x +
Expand Down Expand Up @@ -785,6 +853,32 @@ void DrawToolbar(ImVec2 button_size) {
#endif
}

// Prompt the user to confirm file loading
void DrawDroppedFilesPrompt() {
if (prompt_dropped_file == "") {
return;
}

ImGui::OpenPopup("Open File?");
// Modal window for confirmation
if (ImGui::BeginPopupModal("Open File?", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::Text("Open file \n%s?", prompt_dropped_file.c_str());

if (ImGui::Button("Yes")) {
LoadFile(prompt_dropped_file);
prompt_dropped_file = "";
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("No")) {
Message(""); // Reset last message
prompt_dropped_file = "";
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
}

void SelectObject(
otio::SerializableObject* object,
otio::SerializableObject* context) {
Expand Down Expand Up @@ -829,7 +923,27 @@ void DetectPlayheadLimits() {
void FitZoomWholeTimeline() {
appState.scale = appState.timeline_width / appState.timeline->duration().to_seconds();
}

// GUI utility to add dynamic height to GUI elements

float CalculateDynamicHeight() {
// Get the current font size
float fontSize = ImGui::GetFontSize();
// Get the vertical spacing from the ImGui style
float verticalSpacing = ImGui::GetStyle().ItemSpacing.y;

// Determine how many elements are selected
int visibleElementCount = 0;
if (appState.display_timecode) visibleElementCount++;
if (appState.display_frames) visibleElementCount++;
if (appState.display_seconds) visibleElementCount++;
if (appState.display_rate) visibleElementCount++;

// Set height based on selected elements
// Use fontSize as base height and verticalSpacing for additional height
float calculatedHeight = fontSize + (visibleElementCount - 1) * (fontSize + verticalSpacing);
// Return the maximum of calculatedHeight and the minimum height (10)
return std::max(calculatedHeight, 10.0f);
}
std::string FormattedStringFromTime(otio::RationalTime time, bool allow_rate) {
std::string result;
if (appState.display_timecode) {
Expand Down
3 changes: 3 additions & 0 deletions app.h
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ void Log(const char* format, ...);
void Message(const char* format, ...);
std::string Format(const char* format, ...);

void LoadString(std::string json);

std::string otio_error_string(otio::ErrorStatus const& error_status);

void SelectObject(
Expand All @@ -140,6 +142,7 @@ void SeekPlayhead(double seconds);
void SnapPlayhead();
void DetectPlayheadLimits();
void FitZoomWholeTimeline();
float CalculateDynamicHeight();
std::string FormattedStringFromTime(otio::RationalTime time, bool allow_rate = true);
std::string TimecodeStringFromTime(otio::RationalTime);
std::string FramesStringFromTime(otio::RationalTime);
Expand Down
2 changes: 1 addition & 1 deletion libs/opentimelineio
Submodule opentimelineio updated 166 files
7 changes: 7 additions & 0 deletions main.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,11 @@
void MainInit(int argc, char** argv, int initial_width, int initial_height);
void MainGui();
void MainCleanup();
void FileDropCallback(int count, const char** paths);

#ifdef EMSCRIPTEN
extern "C" {
void js_LoadUrl(char* url);
void js_LoadString(char* json);
}
#endif
48 changes: 45 additions & 3 deletions main_emscripten.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,16 @@
// See https://github.com/ocornut/imgui/pull/2492 as an example on how to do just that.

#include "imgui.h"
#include "imgui_impl_sdl2.h"
#include "imgui_impl_opengl3.h"
#include <stdio.h>
#include <emscripten.h>
#include "imgui_impl_sdl2.h"
#include <SDL.h>
#include <SDL_opengles2.h>
#include <emscripten.h>
#include <emscripten/fetch.h>
#include <iostream>
#include <stdio.h>

#include "app.h"
#include "main.h"

// Emscripten requires to have full control over the main loop. We're going to store our SDL book-keeping variables globally.
Expand Down Expand Up @@ -149,3 +152,42 @@ static void main_loop(void* arg)
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
SDL_GL_SwapWindow(g_Window);
}

void LoadUrlSuccess(emscripten_fetch_t* fetch) {
printf("Finished downloading %llu bytes from URL %s.\n", fetch->numBytes, fetch->url);

std::string otio_string = std::string(fetch->data, fetch->numBytes);
emscripten_fetch_close(fetch);

LoadString(otio_string);

appState.file_path = fetch->url;
}

void LoadUrlFailure(emscripten_fetch_t* fetch) {
printf("Downloading %s failed, HTTP failure status code: %d.\n", fetch->url, fetch->status);
emscripten_fetch_close(fetch);
}

void LoadUrl(std::string url) {
printf("Downloading %s...\n", url.c_str());
emscripten_fetch_attr_t attr;
emscripten_fetch_attr_init(&attr);
strcpy(attr.requestMethod, "GET");
attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY;
// This is async, so we can provide callbacks to handle the result
attr.onsuccess = LoadUrlSuccess;
attr.onerror = LoadUrlFailure;
emscripten_fetch(&attr, url.c_str());
}

extern "C" {
EMSCRIPTEN_KEEPALIVE
void js_LoadUrl(char* url) {
LoadUrl(std::string(url));
}
EMSCRIPTEN_KEEPALIVE
void js_LoadString(char* json) {
LoadString(std::string(json));
}
}
8 changes: 8 additions & 0 deletions main_macos.mm
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@
#import <Metal/Metal.h>
#import <QuartzCore/QuartzCore.h>

// Accept and open a file path
void file_drop_callback(GLFWwindow* window, int count, const char** paths) {
FileDropCallback(count, paths);
}

static void glfw_error_callback(int error, const char* description)
{
fprintf(stderr, "Glfw Error %d: %s\n", error, description);
Expand Down Expand Up @@ -100,6 +105,9 @@ int main(int argc, char** argv)
// Our state
float clear_color[4] = {0.45f, 0.55f, 0.60f, 1.00f};

// Set the drop callback
glfwSetDropCallback(window, file_drop_callback);

// Main loop
while (!glfwWindowShouldClose(window))
{
Expand Down
Loading

0 comments on commit bfc40d6

Please sign in to comment.