Initial commit

This commit is contained in:
Martin Felis
2021-11-11 21:22:24 +01:00
commit b78045ffe7
812 changed files with 421882 additions and 0 deletions
+49
View File
@@ -0,0 +1,49 @@
# Framework library
add_library(sample_framework STATIC
application.h
application.cc
imgui.h
image.h
image.cc
profile.h
profile.cc
renderer.h
utils.h
utils.cc
mesh.h
mesh.cc
internal/camera.h
internal/camera.cc
internal/icosphere.h
internal/immediate.h
internal/immediate.cc
internal/imgui_impl.h
internal/imgui_impl.cc
internal/renderer_impl.h
internal/renderer_impl.cc
internal/shader.h
internal/shader.cc
internal/shooter.h
internal/shooter.cc)
# Samples requires OpenGL package.
if(NOT EMSCRIPTEN)
add_subdirectory(${PROJECT_SOURCE_DIR}/extern/glfw glfw)
target_link_libraries(sample_framework
glfw)
endif()
target_link_libraries(sample_framework
ozz_geometry
ozz_animation_offline
ozz_options)
if(TARGET BUILD_DATA_SAMPLE)
add_dependencies(sample_framework BUILD_DATA_SAMPLE)
endif()
set_target_properties(sample_framework
PROPERTIES FOLDER "samples")
add_subdirectory(tools)
+721
View File
@@ -0,0 +1,721 @@
//----------------------------------------------------------------------------//
// //
// ozz-animation is hosted at http://github.com/guillaumeblanc/ozz-animation //
// and distributed under the MIT License (MIT). //
// //
// Copyright (c) Guillaume Blanc //
// //
// Permission is hereby granted, free of charge, to any person obtaining a //
// copy of this software and associated documentation files (the "Software"), //
// to deal in the Software without restriction, including without limitation //
// the rights to use, copy, modify, merge, publish, distribute, sublicense, //
// and/or sell copies of the Software, and to permit persons to whom the //
// Software is furnished to do so, subject to the following conditions: //
// //
// The above copyright notice and this permission notice shall be included in //
// all copies or substantial portions of the Software. //
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL //
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING //
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER //
// DEALINGS IN THE SOFTWARE. //
// //
//----------------------------------------------------------------------------//
#define OZZ_INCLUDE_PRIVATE_HEADER // Allows to include private headers.
#include "framework/application.h"
#include <cassert>
#include <cstdlib>
#include <cstring>
#ifdef __APPLE__
#include <unistd.h>
#endif // __APPLE__
#if EMSCRIPTEN
#include <emscripten.h>
#include <emscripten/html5.h>
#endif // EMSCRIPTEN
#include "framework/image.h"
#include "framework/internal/camera.h"
#include "framework/internal/imgui_impl.h"
#include "framework/internal/renderer_impl.h"
#include "framework/internal/shooter.h"
#include "framework/profile.h"
#include "framework/renderer.h"
#include "ozz/base/io/stream.h"
#include "ozz/base/log.h"
#include "ozz/base/maths/box.h"
#include "ozz/base/memory/allocator.h"
#include "ozz/options/options.h"
OZZ_OPTIONS_DECLARE_INT(
max_idle_loops,
"The maximum number of idle loops the sample application can perform."
" Application automatically exit when this number of loops is reached."
" A negative value disables this feature.",
-1, false);
OZZ_OPTIONS_DECLARE_BOOL(render, "Enables sample redering.", true, false);
namespace {
// Screen resolution presets.
const ozz::sample::Resolution resolution_presets[] = {
{640, 360}, {640, 480}, {800, 450}, {800, 600}, {1024, 576},
{1024, 768}, {1280, 720}, {1280, 800}, {1280, 960}, {1280, 1024},
{1400, 1050}, {1440, 900}, {1600, 900}, {1600, 1200}, {1680, 1050},
{1920, 1080}, {1920, 1200}};
const int kNumPresets = OZZ_ARRAY_SIZE(resolution_presets);
} // namespace
// Check resolution argument is within 0 - kNumPresets
static bool ResolutionCheck(const ozz::options::Option& _option,
int /*_argc*/) {
const ozz::options::IntOption& option =
static_cast<const ozz::options::IntOption&>(_option);
return option >= 0 && option < kNumPresets;
}
OZZ_OPTIONS_DECLARE_INT_FN(resolution, "Resolution index (0 to 17).", 5, false,
&ResolutionCheck);
namespace ozz {
namespace sample {
Application* Application::application_ = nullptr;
Application::Application()
: exit_(false),
freeze_(false),
fix_update_rate(false),
fixed_update_rate(60.f),
time_factor_(1.f),
time_(0.f),
last_idle_time_(0.),
show_help_(false),
show_grid_(true),
show_axes_(true),
capture_video_(false),
capture_screenshot_(false),
fps_(New<Record>(128)),
update_time_(New<Record>(128)),
render_time_(New<Record>(128)),
resolution_(resolution_presets[0]) {
#ifndef NDEBUG
// Assert presets are correctly sorted.
for (int i = 1; i < kNumPresets; ++i) {
const Resolution& preset_m1 = resolution_presets[i - 1];
const Resolution& preset = resolution_presets[i];
assert(preset.width > preset_m1.width || preset.height > preset_m1.height);
}
#endif // NDEBUG
}
Application::~Application() {}
int Application::Run(int _argc, const char** _argv, const char* _version,
const char* _title) {
// Only one application at a time can be ran.
if (application_) {
return EXIT_FAILURE;
}
application_ = this;
// Starting application
log::Out() << "Starting sample \"" << _title << "\" version \"" << _version
<< "\"" << std::endl;
log::Out() << "Ozz libraries were built with \""
<< math::SimdImplementationName() << "\" SIMD math implementation."
<< std::endl;
// Parse command line arguments.
const char* usage =
"Ozz animation sample. See README.md file for more details.";
ozz::options::ParseResult result =
ozz::options::ParseCommandLine(_argc, _argv, _version, usage);
if (result != ozz::options::kSuccess) {
exit_ = true;
return result == ozz::options::kExitSuccess ? EXIT_SUCCESS : EXIT_FAILURE;
}
// Fetch initial resolution.
resolution_ = resolution_presets[OPTIONS_resolution];
#ifdef __APPLE__
// On OSX, when run from Finder, working path is the root path. This does not
// allow to load resources from relative path.
// The workaround is to change the working directory to application directory.
// The proper solution would probably be to use bundles and load data from
// resource folder.
chdir(ozz::options::ParsedExecutablePath().c_str());
#endif // __APPLE__
// Initialize help.
ParseReadme();
// Open an OpenGL window
bool success = true;
if (OPTIONS_render) {
// Initialize GLFW
if (!glfwInit()) {
application_ = nullptr;
return EXIT_FAILURE;
}
// Setup GL context.
const int gl_version_major = 2, gl_version_minor = 0;
glfwOpenWindowHint(GLFW_OPENGL_VERSION_MAJOR, gl_version_major);
glfwOpenWindowHint(GLFW_OPENGL_VERSION_MINOR, gl_version_minor);
glfwOpenWindowHint(GLFW_FSAA_SAMPLES, 4);
#ifndef NDEBUG
glfwOpenWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE);
#endif // NDEBUG
// Initializes rendering before looping.
if (!glfwOpenWindow(resolution_.width, resolution_.height, 8, 8, 8, 8, 32,
0, GLFW_WINDOW)) {
log::Err() << "Failed to open OpenGL window. Required OpenGL version is "
<< gl_version_major << "." << gl_version_minor << "."
<< std::endl;
success = false;
} else {
log::Out() << "Successfully opened OpenGL window version \""
<< glGetString(GL_VERSION) << "\"." << std::endl;
// Allocates and initializes camera
camera_ = make_unique<internal::Camera>();
math::Float3 camera_center;
math::Float2 camera_angles;
float distance;
if (GetCameraInitialSetup(&camera_center, &camera_angles, &distance)) {
camera_->Reset(camera_center, camera_angles, distance);
}
// Allocates and initializes renderer.
renderer_ = make_unique<internal::RendererImpl>(camera_.get());
success = renderer_->Initialize();
if (success) {
shooter_ = make_unique<internal::Shooter>();
im_gui_ = make_unique<internal::ImGuiImpl>();
#ifndef EMSCRIPTEN // Better not rename web page.
glfwSetWindowTitle(_title);
#endif // EMSCRIPTEN
// Setup the window and installs callbacks.
glfwSwapInterval(1); // Enables vertical sync by default.
glfwSetWindowSizeCallback(&ResizeCbk);
glfwSetWindowCloseCallback(&CloseCbk);
// Loop the sample.
success = Loop();
shooter_.reset();
im_gui_.reset();
}
renderer_.reset();
camera_.reset();
}
// Closes window and terminates GLFW.
glfwTerminate();
} else {
// Loops without any rendering initialization.
success = Loop();
}
// Notifies that an error occurred.
if (!success) {
log::Err() << "An error occurred during sample execution." << std::endl;
}
application_ = nullptr;
return success ? EXIT_SUCCESS : EXIT_FAILURE;
}
// Helper function to detecte key pressed and released.
template <int _Key>
bool KeyPressed() {
static int previous_key = glfwGetKey(_Key);
const int key = glfwGetKey(_Key);
const bool pressed = previous_key == GLFW_PRESS && key == GLFW_RELEASE;
previous_key = key;
return pressed;
}
Application::LoopStatus Application::OneLoop(int _loops) {
Profiler profile(fps_.get()); // Profiles frame.
// Tests for a manual exit request.
if (exit_ || glfwGetKey(GLFW_KEY_ESC) == GLFW_PRESS) {
return kBreak;
}
// Test for an exit request.
if (OPTIONS_max_idle_loops > 0 && _loops > OPTIONS_max_idle_loops) {
return kBreak;
}
// Don't overload the cpu if the window is not active.
#ifndef EMSCRIPTEN
if (OPTIONS_render && !glfwGetWindowParam(GLFW_ACTIVE)) {
glfwWaitEvents(); // Wait...
// Reset last update time in order to stop the time while the app isn't
// active.
last_idle_time_ = glfwGetTime();
return kContinue; // ...but don't do anything.
}
#else
int width, height;
if (emscripten_get_canvas_element_size(nullptr, &width, &height) !=
EMSCRIPTEN_RESULT_SUCCESS) {
return kBreakFailure;
}
if (width != resolution_.width || height != resolution_.height) {
ResizeCbk(width, height);
}
#endif // EMSCRIPTEN
// Enable/disable help on F1 key.
show_help_ = show_help_ ^ KeyPressed<GLFW_KEY_F1>();
// Capture screenshot or video.
capture_screenshot_ = KeyPressed<'S'>();
capture_video_ = capture_video_ ^ KeyPressed<'V'>();
// Do the main loop.
if (!Idle(_loops == 0)) {
return kBreakFailure;
}
// Skips display if "no_render" option is enabled.
if (OPTIONS_render) {
if (!Display()) {
return kBreakFailure;
}
}
return kContinue;
}
void OneLoopCbk(void* _arg) {
Application* app = reinterpret_cast<Application*>(_arg);
static int loops = 0;
app->OneLoop(loops++);
}
bool Application::Loop() {
// Initialize sample.
bool success = OnInitialize();
// Emscripten requires to manage the main loop on their own, as browsers don't
// like infinite blocking functions.
#ifdef EMSCRIPTEN
emscripten_set_main_loop_arg(OneLoopCbk, this, 0, 1);
#else // EMSCRIPTEN
// Loops.
for (int loops = 0; success; ++loops) {
const LoopStatus status = OneLoop(loops);
success = status != kBreakFailure;
if (status != kContinue) {
break;
}
}
#endif // EMSCRIPTEN
// De-initialize sample, even in case of initialization failure.
OnDestroy();
return success;
}
bool Application::Display() {
assert(OPTIONS_render);
bool success = true;
{ // Profiles rendering excluding GUI.
Profiler profile(render_time_.get());
GL(ClearDepth(1.f));
GL(ClearColor(.4f, .42f, .38f, 1.f));
GL(Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
// Setup default states
GL(Enable(GL_CULL_FACE));
GL(CullFace(GL_BACK));
GL(Enable(GL_DEPTH_TEST));
GL(DepthMask(GL_TRUE));
GL(DepthFunc(GL_LEQUAL));
// Bind 3D camera matrices.
camera_->Bind3D();
// Forwards display event to the inheriting application.
if (success) {
success = OnDisplay(renderer_.get());
}
} // Ends profiling.
// Renders grid and axes at the end as they are transparent.
if (show_grid_) {
renderer_->DrawGrid(20, 1.f);
}
if (show_axes_) {
renderer_->DrawAxes(ozz::math::Float4x4::identity());
}
// Bind 2D camera matrices.
camera_->Bind2D();
// Forwards gui event to the inheriting application.
if (success) {
success = Gui();
}
// Capture back buffer.
if (capture_screenshot_ || capture_video_) {
shooter_->Capture(GL_BACK);
capture_screenshot_ = false;
}
// Swaps current window.
glfwSwapBuffers();
return success;
}
bool Application::Idle(bool _first_frame) {
// Early out if displaying help.
if (show_help_) {
last_idle_time_ = glfwGetTime();
return true;
}
// Compute elapsed time since last idle, and delta time.
float delta;
double time = glfwGetTime();
if (_first_frame || // Don't take into account time spent initializing.
time == 0.) { // Means glfw isn't initialized (rendering's disabled).
delta = 1.f / 60.f;
} else {
delta = static_cast<float>(time - last_idle_time_);
}
last_idle_time_ = time;
// Update dt, can be scaled, fixed, freezed...
float update_delta;
if (freeze_) {
update_delta = 0.f;
} else {
if (fix_update_rate) {
update_delta = time_factor_ / fixed_update_rate;
} else {
update_delta = delta * time_factor_;
}
}
// Increment current application time
time_ += update_delta;
// Forwards update event to the inheriting application.
bool update_result;
{ // Profiles update scope.
Profiler profile(update_time_.get());
update_result = OnUpdate(update_delta, time_);
}
// Updates screen shooter object.
if (shooter_) {
shooter_->Update();
}
// Update camera model-view matrix.
if (camera_) {
math::Box scene_bounds;
GetSceneBounds(&scene_bounds);
math::Float4x4 camera_transform;
if (GetCameraOverride(&camera_transform)) {
camera_->Update(camera_transform, scene_bounds, delta, _first_frame);
} else {
camera_->Update(scene_bounds, delta, _first_frame);
}
}
return update_result;
}
bool Application::Gui() {
bool success = true;
const float kFormWidth = 200.f;
const float kHelpMargin = 16.f;
// Finds gui area.
const float kGuiMargin = 2.f;
ozz::math::RectInt window_rect(0, 0, resolution_.width, resolution_.height);
// Fills ImGui's input structure.
internal::ImGuiImpl::Inputs input;
int mouse_y;
glfwGetMousePos(&input.mouse_x, &mouse_y);
input.mouse_y = window_rect.height - mouse_y;
input.lmb_pressed = glfwGetMouseButton(GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS;
// Starts frame
im_gui_->BeginFrame(input, window_rect, renderer_.get());
// Downcast to public imgui.
ImGui* im_gui = im_gui_.get();
// Help gui.
{
math::RectFloat rect(kGuiMargin, kGuiMargin,
window_rect.width - kGuiMargin * 2.f,
window_rect.height - kGuiMargin * 2.f);
// Doesn't constrain form is it's opened, so it covers all screen.
ImGui::Form form(im_gui, "Show help", rect, &show_help_, !show_help_);
if (show_help_) {
im_gui->DoLabel(help_.c_str(), ImGui::kLeft, false);
}
}
// Do framework gui.
if (!show_help_ && success &&
window_rect.width > (kGuiMargin + kFormWidth) * 2.f) {
static bool open = true;
math::RectFloat rect(kGuiMargin, kGuiMargin, kFormWidth,
window_rect.height - kGuiMargin * 2.f - kHelpMargin);
ImGui::Form form(im_gui, "Framework", rect, &open, true);
if (open) {
success = FrameworkGui();
}
}
// Do sample gui.
if (!show_help_ && success && window_rect.width > kGuiMargin + kFormWidth) {
static bool open = true;
math::RectFloat rect(window_rect.width - kFormWidth - kGuiMargin,
kGuiMargin, kFormWidth,
window_rect.height - kGuiMargin * 2 - kHelpMargin);
ImGui::Form form(im_gui, "Sample", rect, &open, true);
if (open) {
// Forwards event to the inherited application.
success = OnGui(im_gui);
}
}
// Ends frame
im_gui_->EndFrame();
return success;
}
bool Application::FrameworkGui() {
// Downcast to public imgui.
ImGui* im_gui = im_gui_.get();
{ // Render statistics
static bool open = true;
ImGui::OpenClose stat_oc(im_gui, "Statistics", &open);
if (open) {
char szLabel[64];
{ // FPS
Record::Statistics statistics = fps_->GetStatistics();
std::sprintf(szLabel, "FPS: %.0f",
statistics.mean == 0.f ? 0.f : 1000.f / statistics.mean);
static bool fps_open = false;
ImGui::OpenClose stats(im_gui, szLabel, &fps_open);
if (fps_open) {
std::sprintf(szLabel, "Frame: %.2f ms", statistics.mean);
im_gui->DoGraph(szLabel, 0.f, statistics.max, statistics.latest,
fps_->cursor(), fps_->record_begin(),
fps_->record_end());
}
}
{ // Update time
Record::Statistics statistics = update_time_->GetStatistics();
std::sprintf(szLabel, "Update: %.2f ms", statistics.mean);
static bool update_open = true; // This is the most relevant for ozz.
ImGui::OpenClose stats(im_gui, szLabel, &update_open);
if (update_open) {
im_gui->DoGraph(nullptr, 0.f, statistics.max, statistics.latest,
update_time_->cursor(), update_time_->record_begin(),
update_time_->record_end());
}
}
{ // Render time
Record::Statistics statistics = render_time_->GetStatistics();
std::sprintf(szLabel, "Render: %.2f ms", statistics.mean);
static bool render_open = false;
ImGui::OpenClose stats(im_gui, szLabel, &render_open);
if (render_open) {
im_gui->DoGraph(nullptr, 0.f, statistics.max, statistics.latest,
render_time_->cursor(), render_time_->record_begin(),
render_time_->record_end());
}
}
}
}
{ // Time control
static bool open = false;
ImGui::OpenClose stats(im_gui, "Time control", &open);
if (open) {
im_gui->DoButton("Freeze", true, &freeze_);
im_gui->DoCheckBox("Fix update rate", &fix_update_rate, true);
if (!fix_update_rate) {
char sz_factor[64];
std::sprintf(sz_factor, "Time factor: %.2f", time_factor_);
im_gui->DoSlider(sz_factor, -5.f, 5.f, &time_factor_);
if (im_gui->DoButton("Reset time factor", time_factor_ != 1.f)) {
time_factor_ = 1.f;
}
} else {
char sz_fixed_update_rate[64];
std::sprintf(sz_fixed_update_rate, "Update rate: %.0f fps",
fixed_update_rate);
im_gui->DoSlider(sz_fixed_update_rate, 1.f, 200.f, &fixed_update_rate,
.5f, true);
if (im_gui->DoButton("Reset update rate", fixed_update_rate != 60.f)) {
fixed_update_rate = 60.f;
}
}
}
}
{ // Rendering options
static bool open = false;
ImGui::OpenClose options(im_gui, "Options", &open);
if (open) {
// Multi-sampling.
static bool fsaa_available = glfwGetWindowParam(GLFW_FSAA_SAMPLES) != 0;
static bool fsaa_enabled = fsaa_available;
if (im_gui->DoCheckBox("Anti-aliasing", &fsaa_enabled, fsaa_available)) {
if (fsaa_enabled) {
GL(Enable(GL_MULTISAMPLE));
} else {
GL(Disable(GL_MULTISAMPLE));
}
}
// Vertical sync
static bool vertical_sync_ = true; // On by default.
if (im_gui->DoCheckBox("Vertical sync", &vertical_sync_, true)) {
glfwSwapInterval(vertical_sync_ ? 1 : 0);
}
im_gui->DoCheckBox("Show grid", &show_grid_, true);
im_gui->DoCheckBox("Show axes", &show_axes_, true);
}
// Searches for matching resolution settings.
int preset_lookup = 0;
for (; preset_lookup < kNumPresets - 1; ++preset_lookup) {
const Resolution& preset = resolution_presets[preset_lookup];
if (preset.width > resolution_.width) {
break;
} else if (preset.width == resolution_.width) {
if (preset.height >= resolution_.height) {
break;
}
}
}
char szResolution[64];
std::sprintf(szResolution, "Resolution: %dx%d", resolution_.width,
resolution_.height);
if (im_gui->DoSlider(szResolution, 0, kNumPresets - 1, &preset_lookup)) {
// Resolution changed.
resolution_ = resolution_presets[preset_lookup];
glfwSetWindowSize(resolution_.width, resolution_.height);
}
}
{ // Capture
static bool open = false;
ImGui::OpenClose controls(im_gui, "Capture", &open);
if (open) {
im_gui->DoButton("Capture video", true, &capture_video_);
capture_screenshot_ |= im_gui->DoButton(
"Capture screenshot", !capture_video_, &capture_screenshot_);
}
}
{ // Controls
static bool open = false;
ImGui::OpenClose controls(im_gui, "Camera controls", &open);
if (open) {
camera_->OnGui(im_gui);
}
}
return true;
}
bool Application::GetCameraInitialSetup(math::Float3* _center,
math::Float2* _angles,
float* _distance) const {
(void)_center;
(void)_angles;
(void)_distance;
return false;
}
// Default implementation doesn't override camera location.
bool Application::GetCameraOverride(math::Float4x4* _transform) const {
(void)_transform;
assert(_transform);
return false;
}
void Application::ResizeCbk(int _width, int _height) {
// Stores new resolution settings.
application_->resolution_.width = _width;
application_->resolution_.height = _height;
// Uses the full viewport.
GL(Viewport(0, 0, _width, _height));
// Forwards screen size to camera and shooter.
application_->camera_->Resize(_width, _height);
application_->shooter_->Resize(_width, _height);
}
int Application::CloseCbk() {
application_->exit_ = true;
return GL_FALSE; // The window will be closed while exiting the main loop.
}
void Application::ParseReadme() {
const char* error_message = "Unable to find README.md help file.";
// Get README file, opens as binary to avoid conversions.
ozz::io::File file("README.md", "rb");
if (!file.opened()) {
help_ = error_message;
return;
}
// Allocate enough space to store the whole file.
const size_t read_length = file.Size();
ozz::memory::Allocator* allocator = ozz::memory::default_allocator();
char* content = reinterpret_cast<char*>(allocator->Allocate(read_length, 4));
// Read the content
if (file.Read(content, read_length) == read_length) {
help_ = ozz::string(content, content + read_length);
} else {
help_ = error_message;
}
// Deallocate temporary buffer;
allocator->Deallocate(content);
}
} // namespace sample
} // namespace ozz
+234
View File
@@ -0,0 +1,234 @@
//----------------------------------------------------------------------------//
// //
// ozz-animation is hosted at http://github.com/guillaumeblanc/ozz-animation //
// and distributed under the MIT License (MIT). //
// //
// Copyright (c) Guillaume Blanc //
// //
// Permission is hereby granted, free of charge, to any person obtaining a //
// copy of this software and associated documentation files (the "Software"), //
// to deal in the Software without restriction, including without limitation //
// the rights to use, copy, modify, merge, publish, distribute, sublicense, //
// and/or sell copies of the Software, and to permit persons to whom the //
// Software is furnished to do so, subject to the following conditions: //
// //
// The above copyright notice and this permission notice shall be included in //
// all copies or substantial portions of the Software. //
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL //
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING //
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER //
// DEALINGS IN THE SOFTWARE. //
// //
//----------------------------------------------------------------------------//
#ifndef OZZ_SAMPLES_FRAMEWORK_APPLICATION_H_
#define OZZ_SAMPLES_FRAMEWORK_APPLICATION_H_
#include <cstddef>
#include "ozz/base/containers/string.h"
#include "ozz/base/memory/unique_ptr.h"
namespace ozz {
namespace math {
struct Box;
struct Float2;
struct Float3;
struct Float4x4;
} // namespace math
namespace sample {
class ImGui;
class Renderer;
class Record;
namespace internal {
class ImGuiImpl;
class RendererImpl;
class Camera;
class Shooter;
} // namespace internal
// Screen resolution settings.
struct Resolution {
int width;
int height;
};
class Application {
public:
// Creates a window and initialize GL context.
// Any failure during initialization or loop execution will be silently
// handled, until the call to ::Run that will return EXIT_FAILURE.
Application();
// Destroys the application. Cleans up everything.
virtual ~Application();
// Runs application main loop.
// Caller must provide main function arguments, as well as application version
// and usage strings.
// Returns EXIT_SUCCESS if the application exits due to user request, or
// EXIT_FAILURE if an error occurred during initialization or the main loop.
// Only one application can be run at a time, otherwise EXIT_FAILURE is
// returned.
int Run(int _argc, const char** _argv, const char* _version,
const char* _title);
private:
// Provides initialization event to the inheriting application. Called while
// the help screen is being displayed.
// OnInitialize can return false which will in turn skip the display loop and
// exit the application with EXIT_FAILURE. Note that OnDestroy is called in
// any case.
virtual bool OnInitialize() = 0;
// Provides de-initialization event to the inheriting application.
// OnDestroy is called even if OnInitialize failed and returned an error.
virtual void OnDestroy() = 0;
// Provides update event to the inheriting application.
// _dt is the elapsed time (in seconds) since the last update.
// _time is application time including scaling (aka accumulated _dt).
// OnUpdate can return false which will in turn stop the loop and exit the
// application with EXIT_FAILURE. Note that OnDestroy is called in any case.
virtual bool OnUpdate(float _dt, float _time) = 0;
// Provides immediate mode gui display event to the inheriting application.
// This function is called in between the OnDisplay and swap functions.
// OnGui can return false which will in turn stop the loop and exit the
// application with EXIT_FAILURE. Note that OnDestroy is called in any case.
virtual bool OnGui(ImGui* _im_gui) = 0;
// Provides display event to the inheriting application.
// This function is called in between the clear and swap functions.
// OnDisplay can return false which will in turn stop the loop and exit the
// application with EXIT_FAILURE. Note that OnDestroy is called in any case.
virtual bool OnDisplay(Renderer* _renderer) = 0;
// Initial camera values. These will only be considered if function returns
// true;
virtual bool GetCameraInitialSetup(math::Float3* _center,
math::Float2* _angles,
float* _distance) const;
// Allows the inheriting application to override camera location.
// Application should return true (false by default) if it wants to override
// Camera location, and fills in this case _transform matrix.
// This function is never called before a first OnUpdate.
virtual bool GetCameraOverride(math::Float4x4* _transform) const;
// Requires the inheriting application to provide scene bounds. It is used by
// the camera to frame all the scene.
// This function is never called before a first OnUpdate.
// If _bound is set to "invalid", then camera won't be updated.
virtual void GetSceneBounds(math::Box* _bound) const = 0;
// Implements framework internal loop function.
bool Loop();
// This callback has to forward loop call to OneLoop private function.
friend void OneLoopCbk(void*);
// Implements framework internal one iteration loop function.
enum LoopStatus {
kContinue, // Can continue with next loop.
kBreak, // Should stop looping (ex: exit).
kBreakFailure, // // Should stop looping beacause something went wrong.
};
LoopStatus OneLoop(int _loops);
// Implements framework internal idle function.
// Returns the value returned by OnIdle or from an internal issue.
bool Idle(bool _first_frame);
// Implements framework internal display callback.
// Returns the value returned by OnDisplay or from an internal issue.
bool Display();
// Implements framework internal gui callback.
// Returns the value returned by OnGui or from an internal error.
bool Gui();
// Implements framework gui rendering.
bool FrameworkGui();
// Implements framework glfw window reshape callback.
static void ResizeCbk(int _width, int _height);
// Implements framework glfw window close callback.
static int CloseCbk();
// Get README.md for content to display it in the help ui.
void ParseReadme();
// Disallow copy and assignment.
Application(const Application& _application);
void operator=(const Application& _application);
// A pointer to the current, and only, running application.
static Application* application_;
// Application exit request.
bool exit_;
// Update time freeze state.
bool freeze_;
// Fixes update rat to a fixed value, instead of real_time.
bool fix_update_rate;
// Fixed update rate, only applies to application update dt, not the real fps.
float fixed_update_rate;
// Update time scale factor.
float time_factor_;
// Current application time, including scaling and freezes..
float time_;
// Last time the idle function was called, in seconds.
// This is a double value in order to maintain enough accuracy when the
// application is running since a long time.
double last_idle_time_;
// The camera object used by the application.
unique_ptr<internal::Camera> camera_;
// The screen shooter object used by the application.
unique_ptr<internal::Shooter> shooter_;
// Set to true to display help.
bool show_help_;
// Grid display settings.
bool show_grid_;
bool show_axes_;
// Capture settings.
bool capture_video_;
bool capture_screenshot_;
// The renderer utility object used by the application.
unique_ptr<internal::RendererImpl> renderer_;
// Immediate mode gui interface.
unique_ptr<internal::ImGuiImpl> im_gui_;
// Timing records.
unique_ptr<Record> fps_;
unique_ptr<Record> update_time_;
unique_ptr<Record> render_time_;
// Current screen resolution.
Resolution resolution_;
// Help message.
ozz::string help_;
};
} // namespace sample
} // namespace ozz
#endif // OZZ_SAMPLES_FRAMEWORK_APPLICATION_H_
+222
View File
@@ -0,0 +1,222 @@
//----------------------------------------------------------------------------//
// //
// ozz-animation is hosted at http://github.com/guillaumeblanc/ozz-animation //
// and distributed under the MIT License (MIT). //
// //
// Copyright (c) Guillaume Blanc //
// //
// Permission is hereby granted, free of charge, to any person obtaining a //
// copy of this software and associated documentation files (the "Software"), //
// to deal in the Software without restriction, including without limitation //
// the rights to use, copy, modify, merge, publish, distribute, sublicense, //
// and/or sell copies of the Software, and to permit persons to whom the //
// Software is furnished to do so, subject to the following conditions: //
// //
// The above copyright notice and this permission notice shall be included in //
// all copies or substantial portions of the Software. //
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL //
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING //
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER //
// DEALINGS IN THE SOFTWARE. //
// //
//----------------------------------------------------------------------------//
#include "framework/image.h"
#include "ozz/base/io/stream.h"
#include "ozz/base/log.h"
#include "ozz/base/memory/allocator.h"
namespace ozz {
namespace sample {
namespace image {
bool HasAlpha(Format::Value _format) { return _format >= Format::kRGBA; }
int Stride(Format::Value _format) { return _format <= Format::kBGR ? 3 : 4; }
#define PUSH_PIXEL_RGB(_buffer, _size, _repetition, _pixel, _mapping) \
_buffer[_size + 0] = 0x80 | ((_repetition - 1) & 0xff); \
_buffer[_size + 1] = _pixel.c[_mapping[0]]; \
_buffer[_size + 2] = _pixel.c[_mapping[1]]; \
_buffer[_size + 3] = _pixel.c[_mapping[2]]; \
_size += 4;
#define PUSH_PIXEL_RGBA(_buffer, _size, _repetition, _pixel, _mapping) \
_buffer[_size + 0] = 0x80 | ((_repetition - 1) & 0xff); \
_buffer[_size + 1] = _pixel.c[_mapping[0]]; \
_buffer[_size + 2] = _pixel.c[_mapping[1]]; \
_buffer[_size + 3] = _pixel.c[_mapping[2]]; \
_buffer[_size + 4] = _pixel.c[_mapping[3]]; \
_size += 5;
bool WriteTGA(const char* _filename, int _width, int _height,
Format::Value _src_format, const uint8_t* _src_buffer,
bool _write_alpha) {
union Pixel {
uint8_t c[4];
uint32_t p;
};
assert(_filename && _src_buffer);
ozz::log::LogV() << "Write image to TGA file \"" << _filename << "\"."
<< std::endl;
// Opens output file.
ozz::io::File file(_filename, "wb");
if (!file.opened()) {
ozz::log::Err() << "Failed to open file \"" << _filename
<< "\" for writing." << std::endl;
return false;
}
// Builds and writes tga header.
const uint8_t header[] = {
0, // ID length
0, // Color map type
10, // Image type (RLE true-color)
0,
0,
0,
0,
0, // Color map specification (no color map)
0,
0, // X-origin (2 bytes little-endian)
0,
0, // Y-origin (2 bytes little-endian)
static_cast<uint8_t>(_width & 0xff), // Width (2 bytes little-endian)
static_cast<uint8_t>((_width >> 8) & 0xff),
static_cast<uint8_t>(_height & 0xff), // Height (2 bytes little-endian)
static_cast<uint8_t>((_height >> 8) & 0xff),
static_cast<uint8_t>(_write_alpha ? 32 : 24), // Pixel depth
0}; // Image descriptor
static_assert(sizeof(header) == 18, "Expects 18 bytes structure.");
file.Write(header, sizeof(header));
// Early out if no pixel to write.
if (!_width || !_height) {
return true;
}
// Writes pixels, with RLE compression.
// Prepares component mappings from src to TARGA format.
const uint8_t mappings[4][4] = {
{2, 1, 0, 0}, {0, 1, 2, 0}, {2, 1, 0, 3}, {0, 1, 2, 3}};
const uint8_t* mapping = mappings[_src_format];
// Allocates enough space to store RLE packets for the worst case scenario.
uint8_t* dest_buffer =
reinterpret_cast<uint8_t*>(ozz::memory::default_allocator()->Allocate(
(1 + (_write_alpha ? 4 : 3)) * _width * _height, 4));
size_t dest_size = 0;
if (HasAlpha(_src_format)) {
assert(Stride(_src_format) == 4);
const int src_pitch = _width * 4;
const int src_size = _height * src_pitch;
if (_write_alpha) {
for (int line = 0; line < src_size; line += src_pitch) {
Pixel current = {{_src_buffer[line + 0], _src_buffer[line + 1],
_src_buffer[line + 2], _src_buffer[line + 3]}};
int count = 1;
for (int p = line + 4; p < line + _width * 4; p += 4, count++) {
const Pixel next = {{_src_buffer[p + 0], _src_buffer[p + 1],
_src_buffer[p + 2], _src_buffer[p + 3]}};
if (current.p != next.p || count == 128) {
// Writes current packet.
PUSH_PIXEL_RGBA(dest_buffer, dest_size, count, current, mapping);
// Starts new RLE packet.
current.p = next.p;
count = 0;
}
}
// Finishes the line.
assert(count > 0 && count <= 128);
PUSH_PIXEL_RGBA(dest_buffer, dest_size, count, current, mapping);
}
} else {
for (int line = 0; line < src_size; line += src_pitch) {
Pixel current = {{_src_buffer[line + 0], _src_buffer[line + 1],
_src_buffer[line + 2], 0}};
int count = 1;
for (int p = line + 4; p < line + _width * 4; p += 4, count++) {
const Pixel next = {
{_src_buffer[p + 0], _src_buffer[p + 1], _src_buffer[p + 2], 0}};
if (current.p != next.p || count == 128) {
// Writes current packet.
PUSH_PIXEL_RGB(dest_buffer, dest_size, count, current, mapping);
// Starts new RLE packet.
current.p = next.p;
count = 0;
}
}
// Finishes the line.
assert(count > 0 && count <= 128);
PUSH_PIXEL_RGB(dest_buffer, dest_size, count, current, mapping);
}
}
} else { // Source has no alpha channel.
assert(Stride(_src_format) == 3);
const int src_pitch = _width * 3;
const int src_size = _height * src_pitch;
if (_write_alpha) {
for (int line = 0; line < src_size; line += src_pitch) {
Pixel current = {{_src_buffer[line + 0], _src_buffer[line + 1],
_src_buffer[line + 2], 255}};
int count = 1;
for (int p = line + 3; p < line + _width * 3; p += 3, count++) {
const Pixel next = {{_src_buffer[p + 0], _src_buffer[p + 1],
_src_buffer[p + 2], 255}};
if (current.p != next.p || count == 128) {
// Writes current packet.
PUSH_PIXEL_RGBA(dest_buffer, dest_size, count, current, mapping);
// Starts new RLE packet.
current.p = next.p;
count = 0;
}
}
// Finishes the line.
assert(count > 0 && count <= 128);
PUSH_PIXEL_RGBA(dest_buffer, dest_size, count, current, mapping);
}
} else {
for (int line = 0; line < src_size; line += src_pitch) {
Pixel current = {{_src_buffer[line + 0], _src_buffer[line + 1],
_src_buffer[line + 2], 0}};
int count = 1;
for (int p = line + 3; p < line + _width * 3; p += 3, count++) {
const Pixel next = {
{_src_buffer[p + 0], _src_buffer[p + 1], _src_buffer[p + 2], 0}};
if (current.p != next.p || count == 128) {
// Writes current packet.
PUSH_PIXEL_RGB(dest_buffer, dest_size, count, current, mapping);
// Starts new RLE packet.
current.p = next.p;
count = 0;
}
}
// Finishes the line.
assert(count > 0 && count <= 128);
PUSH_PIXEL_RGB(dest_buffer, dest_size, count, current, mapping);
}
}
}
// Writes all the RLE packets buffer at once.
file.Write(dest_buffer, dest_size);
ozz::memory::default_allocator()->Deallocate(dest_buffer);
return true;
}
#undef PUSH_PIXEL_RGB
#undef PUSH_PIXEL_RGBA
} // namespace image
} // namespace sample
} // namespace ozz
+63
View File
@@ -0,0 +1,63 @@
//----------------------------------------------------------------------------//
// //
// ozz-animation is hosted at http://github.com/guillaumeblanc/ozz-animation //
// and distributed under the MIT License (MIT). //
// //
// Copyright (c) Guillaume Blanc //
// //
// Permission is hereby granted, free of charge, to any person obtaining a //
// copy of this software and associated documentation files (the "Software"), //
// to deal in the Software without restriction, including without limitation //
// the rights to use, copy, modify, merge, publish, distribute, sublicense, //
// and/or sell copies of the Software, and to permit persons to whom the //
// Software is furnished to do so, subject to the following conditions: //
// //
// The above copyright notice and this permission notice shall be included in //
// all copies or substantial portions of the Software. //
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL //
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING //
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER //
// DEALINGS IN THE SOFTWARE. //
// //
//----------------------------------------------------------------------------//
#ifndef OZZ_SAMPLES_FRAMEWORK_IMAGE_H_
#define OZZ_SAMPLES_FRAMEWORK_IMAGE_H_
// Implements image files read/write.
#include "ozz/base/platform.h"
namespace ozz {
namespace sample {
namespace image {
// Pixel format definition.
struct Format {
enum Value {
kRGB,
kBGR,
kRGBA,
kBGRA,
};
};
// Tests if format specification contains alpha channel.
bool HasAlpha(Format::Value _format);
// Get stride from format specification.
int Stride(Format::Value _format);
// Writes as TARGA image to file _filename.
bool WriteTGA(const char* _filename, int _width, int _height,
Format::Value _src_format, const uint8_t* _src_buffer,
bool _write_alpha);
} // namespace image
} // namespace sample
} // namespace ozz
#endif // OZZ_SAMPLES_FRAMEWORK_IMAGE_H_
+186
View File
@@ -0,0 +1,186 @@
//----------------------------------------------------------------------------//
// //
// ozz-animation is hosted at http://github.com/guillaumeblanc/ozz-animation //
// and distributed under the MIT License (MIT). //
// //
// Copyright (c) Guillaume Blanc //
// //
// Permission is hereby granted, free of charge, to any person obtaining a //
// copy of this software and associated documentation files (the "Software"), //
// to deal in the Software without restriction, including without limitation //
// the rights to use, copy, modify, merge, publish, distribute, sublicense, //
// and/or sell copies of the Software, and to permit persons to whom the //
// Software is furnished to do so, subject to the following conditions: //
// //
// The above copyright notice and this permission notice shall be included in //
// all copies or substantial portions of the Software. //
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL //
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING //
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER //
// DEALINGS IN THE SOFTWARE. //
// //
//----------------------------------------------------------------------------//
#ifndef OZZ_SAMPLES_FRAMEWORK_IMGUI_H_
#define OZZ_SAMPLES_FRAMEWORK_IMGUI_H_
#include <cstdio>
namespace ozz {
namespace math {
struct RectFloat;
}
namespace sample {
// Interface for immediate mode graphical user interface rendering.
class ImGui {
public:
// Declares a virtual destructor to allow proper destruction.
virtual ~ImGui() {}
// Text justification types.
enum Justification {
kLeft,
kCenter,
kRight,
};
// Begins a new form of size _rect.
// This object uses the RAII mechanism to ensure begin/end symmetry.
// A form is a root in the frame's container stack.
// The _rect argument is relative to the parent's rect and is automatically
// shrunk to fit inside parent's rect and to the size of its widgets.
// Providing a non nullptr _title argument displays a title on top of the form.
// Providing a non nullptr _open argument enables the open/close mechanism.
class Form {
public:
Form(ImGui* _im_gui, const char* _title, const math::RectFloat& _rect,
bool* _open, bool _constrain)
: im_gui_(_im_gui) {
im_gui_->BeginContainer(_title, &_rect, _open, _constrain);
}
~Form() { im_gui_->EndContainer(); }
private:
Form(const Form&); // Forbids copy.
void operator=(const Form&); // Forbids assignment.
ImGui* im_gui_;
};
// Begins a new open-close widget in the parent's rect, ie: a form or an other
// open-close.
// This object uses the RAII mechanism to ensure open/close symmetry.
// Providing a non nullptr _title argument displays a title on top of the
// open-close.
// Providing a non nullptr _open argument enables the open/close mechanism.
class OpenClose {
public:
OpenClose(ImGui* _im_gui, const char* _title, bool* _open)
: im_gui_(_im_gui) {
im_gui_->BeginContainer(_title, nullptr, _open, true);
}
~OpenClose() { im_gui_->EndContainer(); }
private:
OpenClose(const OpenClose&); // Forbids copy.
void operator=(const OpenClose&); // Forbids assignment.
ImGui* im_gui_;
};
// Adds a button to the current context and returns true if it was clicked.
// If _enabled is false then interactions with the button are disabled, and
// rendering is grayed out.
// If _state is not nullptr, then it is used as an in-out parameter to set and
// store button's state. The button can then behave like a check box, with
// a button rendering style.
// It allows for example to
// Returns true is button was clicked.
virtual bool DoButton(const char* _label, bool _enabled = true,
bool* _state = nullptr) = 0;
// Adds a float slider to the current context and returns true if _value was
// modified.
// _value is the in-out parameter that stores slider value. It's clamped
// between _min and _max bounds.
// _pow is used to modify slider's scale. It can be used to give a higher
// precision to low or high values according to _pow.
// If _enabled is false then interactions with the slider are disabled, and
// rendering is grayed out.
// Returns true if _value _value has changed.
virtual bool DoSlider(const char* _label, float _min, float _max,
float* _value, float _pow = 1.f,
bool _enabled = true) = 0;
// Adds an integer slider to the current context and returns true if _value
// was modified.
// _value is the in-out parameter that stores slider value. It's clamped
// between _min and _max bounds.
// _pow is used to modify slider's scale. It can be used to give a higher
// precision to low or high values according to _pow.
// If _enabled is false then interactions with the slider are disabled, and
// rendering is grayed out.
// Returns true if _value _value has changed.
virtual bool DoSlider(const char* _label, int _min, int _max, int* _value,
float _pow = 1.f, bool _enabled = true) = 0;
// Adds a check box to the current context and returns true if it has been
// modified. Used to represent boolean value.
// _state is the in-out parameter that stores check box state.
// If _enabled is false then interactions with the slider are disabled, and
// rendering is grayed out.
// Returns true if _value _state has changed.
virtual bool DoCheckBox(const char* _label, bool* _state,
bool _enabled = true) = 0;
// Adds a radio button to the current context and returns true if it has been
// modified. Used to represent a possible reference _ref value taken by the
// current value _value.
// Displays a "checked" radio button if _ref si equal to th selected _value.
// Returns true if _value _value has changed.
virtual bool DoRadioButton(int _ref, const char* _label, int* _value,
bool _enabled = true) = 0;
// Adds a text label to the current context. Its height depends on the number
// of lines.
// _justification selects the text alignment in the current container.
// if _single_line is true then _label text is cut at the end of the first
// line.
virtual void DoLabel(const char* _label, Justification _justification = kLeft,
bool _single_line = true) = 0;
// Adds a graph widget to the current context.
// Displays values from the right (newest value) to the left (latest).
// The range of value is described by _value_begin, _value_end. _value_cursor
// allows using a linear or circular buffer of values. Set _value_cursor to
// _value_begin to use a linear buffer of range [_value_begin,_value_end[. Or
// set _value_cursor of a circular buffer such that range [_value_cursor,
// _value_end[ and [_value_begin,_value_cursor[ are used.
// All values outside of _min and _max range are clamped.
// If _label is not nullptr then a text is displayed on top of the graph.
virtual void DoGraph(const char* _label, float _min, float _max, float _mean,
const float* _value_cursor, const float* _value_begin,
const float* _value_end) = 0;
private:
// Begins a new container of size _rect.
// Widgets (buttons, sliders...) can only be displayed in a container.
// The rectangles height is the maximum height that the container can use. The
// container automatically shrinks to fit the size of the widgets it contains.
// Providing a non nullptr _title argument displays a title on top of the
// container.
// Providing a nullptr _rect argument means that the container will use all its
// parent size.
// Providing a non nullptr _open argument enables the open/close mechanism.
virtual void BeginContainer(const char* _title, const math::RectFloat* _rect,
bool* _open, bool _constrain) = 0;
// Ends the current container.
virtual void EndContainer() = 0;
};
} // namespace sample
} // namespace ozz
#endif // OZZ_SAMPLES_FRAMEWORK_IMGUI_H_
@@ -0,0 +1,279 @@
//----------------------------------------------------------------------------//
// //
// ozz-animation is hosted at http://github.com/guillaumeblanc/ozz-animation //
// and distributed under the MIT License (MIT). //
// //
// Copyright (c) Guillaume Blanc //
// //
// Permission is hereby granted, free of charge, to any person obtaining a //
// copy of this software and associated documentation files (the "Software"), //
// to deal in the Software without restriction, including without limitation //
// the rights to use, copy, modify, merge, publish, distribute, sublicense, //
// and/or sell copies of the Software, and to permit persons to whom the //
// Software is furnished to do so, subject to the following conditions: //
// //
// The above copyright notice and this permission notice shall be included in //
// all copies or substantial portions of the Software. //
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL //
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING //
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER //
// DEALINGS IN THE SOFTWARE. //
// //
//----------------------------------------------------------------------------//
#define OZZ_INCLUDE_PRIVATE_HEADER // Allows to include private headers.
#include "camera.h"
#include "ozz/base/log.h"
#include "ozz/base/maths/box.h"
#include "ozz/base/maths/math_constant.h"
#include "ozz/base/maths/math_ex.h"
#include "ozz/base/platform.h"
#include "framework/application.h"
#include "framework/imgui.h"
#include "renderer_impl.h"
using ozz::math::Float2;
using ozz::math::Float3;
using ozz::math::Float4x4;
namespace ozz {
namespace sample {
namespace internal {
// Declares camera navigation constants.
const float kDefaultDistance = 8.f;
const Float3 kDefaultCenter = Float3(0.f, .5f, 0.f);
const Float2 kDefaultAngle =
Float2(-ozz::math::kPi * 1.f / 12.f, ozz::math::kPi * 1.f / 5.f);
const float kAngleFactor = .01f;
const float kDistanceFactor = .1f;
const float kScrollFactor = .03f;
const float kPanFactor = .05f;
const float kKeyboardFactor = 100.f;
const float kNear = .01f;
const float kFar = 1000.f;
const float kFovY = ozz::math::kPi / 3.f;
const float kFrameAllZoomOut = 1.3f; // 30% bigger than the scene.
// Setups initial values.
Camera::Camera()
: projection_(Float4x4::identity()),
projection_2d_(Float4x4::identity()),
view_(Float4x4::identity()),
view_proj_(Float4x4::identity()),
angles_(kDefaultAngle),
center_(kDefaultCenter),
distance_(kDefaultDistance),
mouse_last_x_(0),
mouse_last_y_(0),
mouse_last_wheel_(0),
auto_framing_(true) {}
Camera::~Camera() {}
void Camera::Update(const math::Box& _box, float _delta_time,
bool _first_frame) {
// Frame the scene according to the provided box.
if (_box.is_valid()) {
if (auto_framing_ || _first_frame) {
center_ = (_box.max + _box.min) * .5f;
if (_first_frame) {
const float radius = Length(_box.max - _box.min) * .5f;
distance_ = radius * kFrameAllZoomOut / tanf(kFovY * .5f);
}
}
}
// Update manual controls.
const Controls controls = UpdateControls(_delta_time);
// Disable autoframing according to inputs.
auto_framing_ &=
!controls.panning && !controls.zooming && !controls.zooming_wheel;
}
void Camera::Update(const math::Float4x4& _transform, const math::Box& _box,
float _delta_time, bool _first_frame) {
// Extract distance and angles such that theu are coherent when switching out
// of auto_framing_.
if (_box.is_valid()) {
if (auto_framing_ || _first_frame) {
// Extract components from the view martrix.
ozz::math::Float3 camera_dir;
ozz::math::Store3PtrU(-ozz::math::Normalize3(_transform.cols[2]),
&camera_dir.x);
ozz::math::Float3 camera_pos;
ozz::math::Store3PtrU(_transform.cols[3], &camera_pos.x);
// Arbitrary decides that distance (focus point) is from camera to scene
// center.
const ozz::math::Float3 box_center_ = (_box.max + _box.min) * .5f;
distance_ = Length(box_center_ - camera_pos);
center_ = camera_pos + camera_dir * distance_;
angles_.x = asinf(camera_dir.y);
angles_.y = atan2(-camera_dir.x, -camera_dir.z);
}
}
// Update manual controls.
const Controls controls = UpdateControls(_delta_time);
// Disable autoframing according to inputs.
auto_framing_ &= !controls.panning && !controls.rotating &&
!controls.zooming && !controls.zooming_wheel;
if (auto_framing_) {
view_ = Invert(_transform);
}
}
Camera::Controls Camera::UpdateControls(float _delta_time) {
Controls controls;
controls.zooming = false;
controls.zooming_wheel = false;
controls.rotating = false;
controls.panning = false;
// Mouse wheel + SHIFT activates Zoom.
if (glfwGetKey(GLFW_KEY_LSHIFT) == GLFW_PRESS) {
const int w = glfwGetMouseWheel();
const int dw = w - mouse_last_wheel_;
mouse_last_wheel_ = w;
if (dw != 0) {
controls.zooming_wheel = true;
distance_ *= 1.f + -dw * kScrollFactor;
}
} else {
mouse_last_wheel_ = glfwGetMouseWheel();
}
// Fetches current mouse position and compute its movement since last frame.
int x, y;
glfwGetMousePos(&x, &y);
const int mdx = x - mouse_last_x_;
const int mdy = y - mouse_last_y_;
mouse_last_x_ = x;
mouse_last_y_ = y;
// Finds keyboard relative dx and dy commmands.
const int timed_factor =
ozz::math::Max(1, static_cast<int>(kKeyboardFactor * _delta_time));
const int kdx =
timed_factor * (glfwGetKey(GLFW_KEY_LEFT) - glfwGetKey(GLFW_KEY_RIGHT));
const int kdy =
timed_factor * (glfwGetKey(GLFW_KEY_DOWN) - glfwGetKey(GLFW_KEY_UP));
const bool keyboard_interact = kdx || kdy;
// Computes composed keyboard and mouse dx and dy.
const int dx = mdx + kdx;
const int dy = mdy + kdy;
// Mouse right button activates Zoom, Pan and Orbit modes.
if (keyboard_interact ||
glfwGetMouseButton(GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS) {
if (glfwGetKey(GLFW_KEY_LSHIFT) == GLFW_PRESS) { // Zoom mode.
controls.zooming = true;
distance_ += dy * kDistanceFactor;
} else if (glfwGetKey(GLFW_KEY_LALT) == GLFW_PRESS) { // Pan mode.
controls.panning = true;
const float dx_pan = -dx * kPanFactor;
const float dy_pan = -dy * kPanFactor;
// Moves along camera axes.
math::Float4x4 transpose = Transpose(view_);
math::Float3 right_transpose, up_transpose;
math::Store3PtrU(transpose.cols[0], &right_transpose.x);
math::Store3PtrU(transpose.cols[1], &up_transpose.x);
center_ = center_ + right_transpose * dx_pan + up_transpose * dy_pan;
} else { // Orbit mode.
controls.rotating = true;
angles_.x = fmodf(angles_.x - dy * kAngleFactor, ozz::math::k2Pi);
angles_.y = fmodf(angles_.y - dx * kAngleFactor, ozz::math::k2Pi);
}
}
// Build the model view matrix components.
const Float4x4 center = Float4x4::Translation(
math::simd_float4::Load(center_.x, center_.y, center_.z, 1.f));
const Float4x4 y_rotation = Float4x4::FromAxisAngle(
math::simd_float4::y_axis(), math::simd_float4::Load1(angles_.y));
const Float4x4 x_rotation = Float4x4::FromAxisAngle(
math::simd_float4::x_axis(), math::simd_float4::Load1(angles_.x));
const Float4x4 distance =
Float4x4::Translation(math::simd_float4::Load(0.f, 0.f, distance_, 1.f));
// Concatenate view matrix components.
view_ = Invert(center * y_rotation * x_rotation * distance);
return controls;
}
void Camera::Reset(const math::Float3& _center, const math::Float2& _angles,
float _distance) {
center_ = _center;
angles_ = _angles;
distance_ = _distance;
}
void Camera::OnGui(ImGui* _im_gui) {
const char* controls_label =
"-RMB: Rotate\n"
"-Shift + Wheel: Zoom\n"
"-Shift + RMB: Zoom\n"
"-Alt + RMB: Pan\n";
_im_gui->DoLabel(controls_label, ImGui::kLeft, false);
_im_gui->DoCheckBox("Automatic", &auto_framing_);
}
void Camera::Bind3D() {
// Updates internal vp matrix.
view_proj_ = projection_ * view_;
}
void Camera::Bind2D() {
// Updates internal vp matrix. View matrix is identity.
view_proj_ = projection_2d_;
}
void Camera::Resize(int _width, int _height) {
// Handle empty windows.
if (_width <= 0 || _height <= 0) {
projection_ = ozz::math::Float4x4::identity();
projection_2d_ = ozz::math::Float4x4::identity();
return;
}
// Compute the 3D projection matrix.
const float ratio = 1.f * _width / _height;
const float h = tan(kFovY * .5f) * kNear;
const float w = h * ratio;
projection_.cols[0] = math::simd_float4::Load(kNear / w, 0.f, 0.f, 0.f);
projection_.cols[1] = math::simd_float4::Load(0.f, kNear / h, 0.f, 0.f);
projection_.cols[2] =
math::simd_float4::Load(0.f, 0.f, -(kFar + kNear) / (kFar - kNear), -1.f);
projection_.cols[3] = math::simd_float4::Load(
0.f, 0.f, -(2.f * kFar * kNear) / (kFar - kNear), 0.f);
// Computes the 2D projection matrix.
projection_2d_.cols[0] = math::simd_float4::Load(2.f / _width, 0.f, 0.f, 0.f);
projection_2d_.cols[1] =
math::simd_float4::Load(0.f, 2.f / _height, 0.f, 0.f);
projection_2d_.cols[2] = math::simd_float4::Load(0.f, 0.f, -2.0f, 0.f);
projection_2d_.cols[3] = math::simd_float4::Load(-1.f, -1.f, 0.f, 1.f);
}
} // namespace internal
} // namespace sample
} // namespace ozz
@@ -0,0 +1,136 @@
//----------------------------------------------------------------------------//
// //
// ozz-animation is hosted at http://github.com/guillaumeblanc/ozz-animation //
// and distributed under the MIT License (MIT). //
// //
// Copyright (c) Guillaume Blanc //
// //
// Permission is hereby granted, free of charge, to any person obtaining a //
// copy of this software and associated documentation files (the "Software"), //
// to deal in the Software without restriction, including without limitation //
// the rights to use, copy, modify, merge, publish, distribute, sublicense, //
// and/or sell copies of the Software, and to permit persons to whom the //
// Software is furnished to do so, subject to the following conditions: //
// //
// The above copyright notice and this permission notice shall be included in //
// all copies or substantial portions of the Software. //
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL //
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING //
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER //
// DEALINGS IN THE SOFTWARE. //
// //
//----------------------------------------------------------------------------//
#ifndef OZZ_SAMPLES_FRAMEWORK_INTERNAL_CAMERA_H_
#define OZZ_SAMPLES_FRAMEWORK_INTERNAL_CAMERA_H_
#ifndef OZZ_INCLUDE_PRIVATE_HEADER
#error "This header is private, it cannot be included from public headers."
#endif // OZZ_INCLUDE_PRIVATE_HEADER
#include "ozz/base/maths/simd_math.h"
#include "ozz/base/maths/vec_float.h"
namespace ozz {
namespace math {
struct Box;
}
namespace sample {
class ImGui;
namespace internal {
// Framework internal implementation of an OpenGL/glfw camera system that can be
// manipulated with the mouse and some shortcuts.
class Camera {
public:
// Initializes camera to its default framing.
Camera();
// Destructor.
~Camera();
// Updates camera framing: mouse manipulation, timed transitions...
// Returns actions that the user applied to the camera during the frame.
void Update(const math::Box& _box, float _delta_time, bool _first_frame);
// Updates camera location, overriding user inputs.
// Returns actions that the user applied to the camera during the frame.
void Update(const math::Float4x4& _transform, const math::Box& _box,
float _delta_time, bool _first_frame);
// Resets camera center, angles and distance.
void Reset(const math::Float3& _center, const math::Float2& _angles,
float _distance);
// Provides immediate mode gui display event.
void OnGui(ImGui* _im_gui);
// Binds 3d projection and view matrices to the current matrix.
void Bind3D();
// Binds 2d projection and view matrices to the current matrix.
void Bind2D();
// Resize notification, used to rebuild projection matrix.
void Resize(int _width, int _height);
// Get the current projection matrix.
const math::Float4x4& projection() { return projection_; }
// Get the current model-view matrix.
const math::Float4x4& view() { return view_; }
// Get the current model-view-projection matrix.
const math::Float4x4& view_proj() { return view_proj_; }
// Set to true to automatically frame the camera on the whole scene.
void set_auto_framing(bool _auto) { auto_framing_ = _auto; }
// Get auto framing state.
bool auto_framing() const { return auto_framing_; }
private:
struct Controls {
bool zooming;
bool zooming_wheel;
bool rotating;
bool panning;
};
Controls UpdateControls(float _delta_time);
// The current projection matrix.
math::Float4x4 projection_;
// The current projection matrix.
math::Float4x4 projection_2d_;
// The current model-view matrix.
math::Float4x4 view_;
// The current model-view-projection matrix.
math::Float4x4 view_proj_;
// The angles in degree of the camera rotation around x and y axes.
math::Float2 angles_;
// The center of the rotation.
math::Float3 center_;
// The view distance, from the center of rotation.
float distance_;
// The position of the mouse, the last time it has been seen.
int mouse_last_x_;
int mouse_last_y_;
int mouse_last_wheel_;
// Set to true to automatically frame the camera on the whole scene.
bool auto_framing_;
};
} // namespace internal
} // namespace sample
} // namespace ozz
#endif // OZZ_SAMPLES_FRAMEWORK_INTERNAL_CAMERA_H_
@@ -0,0 +1,171 @@
//----------------------------------------------------------------------------//
// //
// ozz-animation is hosted at http://github.com/guillaumeblanc/ozz-animation //
// and distributed under the MIT License (MIT). //
// //
// Copyright (c) Guillaume Blanc //
// //
// Permission is hereby granted, free of charge, to any person obtaining a //
// copy of this software and associated documentation files (the "Software"), //
// to deal in the Software without restriction, including without limitation //
// the rights to use, copy, modify, merge, publish, distribute, sublicense, //
// and/or sell copies of the Software, and to permit persons to whom the //
// Software is furnished to do so, subject to the following conditions: //
// //
// The above copyright notice and this permission notice shall be included in //
// all copies or substantial portions of the Software. //
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL //
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING //
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER //
// DEALINGS IN THE SOFTWARE. //
// //
//----------------------------------------------------------------------------//
#ifndef OZZ_SAMPLES_FRAMEWORK_INTERNAL_ICOSPHERE_H_
#define OZZ_SAMPLES_FRAMEWORK_INTERNAL_ICOSPHERE_H_
#include "ozz/base/platform.h"
// Precomputed normalized icosphere with 2 levels of subdivisions.
// Note that kVertices can also be used as normals.
namespace icosphere {
static const float kVertices[] = {
-0.682f, 0.717f, 0.148f, -0.588f, 0.688f, 0.425f, -0.443f, 0.864f,
0.239f, -0.526f, 0.851f, 0.000f, -0.809f, 0.500f, 0.309f, -0.309f,
0.809f, 0.500f, -0.717f, 0.148f, 0.682f, -0.688f, 0.425f, 0.588f,
-0.864f, 0.239f, 0.443f, -0.851f, 0.000f, 0.526f, -0.500f, 0.309f,
0.809f, -0.148f, 0.682f, 0.717f, -0.425f, 0.588f, 0.688f, -0.239f,
0.443f, 0.864f, 0.000f, 0.526f, 0.851f, -0.162f, 0.951f, 0.263f,
-0.295f, 0.955f, 0.000f, 0.000f, 1.000f, 0.000f, 0.148f, 0.682f,
0.717f, 0.000f, 0.851f, 0.526f, 0.309f, 0.809f, 0.500f, 0.295f,
0.955f, 0.000f, 0.162f, 0.951f, 0.263f, 0.443f, 0.864f, 0.239f,
0.526f, 0.851f, 0.000f, -0.162f, 0.951f, -0.263f, -0.443f, 0.864f,
-0.239f, -0.309f, 0.809f, -0.500f, 0.443f, 0.864f, -0.239f, 0.162f,
0.951f, -0.263f, 0.309f, 0.809f, -0.500f, -0.148f, 0.682f, -0.717f,
0.000f, 0.851f, -0.526f, 0.148f, 0.682f, -0.717f, 0.000f, 0.526f,
-0.851f, -0.588f, 0.688f, -0.425f, -0.682f, 0.717f, -0.148f, -0.809f,
0.500f, -0.309f, -0.239f, 0.443f, -0.864f, -0.425f, 0.588f, -0.688f,
-0.500f, 0.309f, -0.809f, -0.864f, 0.239f, -0.443f, -0.688f, 0.425f,
-0.588f, -0.717f, 0.148f, -0.682f, -0.851f, 0.000f, -0.526f, -0.851f,
0.526f, 0.000f, -0.955f, 0.000f, -0.295f, -0.951f, 0.263f, -0.162f,
-1.000f, 0.000f, 0.000f, -0.951f, 0.263f, 0.162f, -0.955f, 0.000f,
0.295f, 0.588f, 0.688f, 0.425f, 0.682f, 0.717f, 0.148f, 0.809f,
0.500f, 0.309f, 0.239f, 0.443f, 0.864f, 0.425f, 0.588f, 0.688f,
0.500f, 0.309f, 0.809f, 0.864f, 0.239f, 0.443f, 0.688f, 0.425f,
0.588f, 0.717f, 0.148f, 0.682f, 0.851f, 0.000f, 0.526f, -0.263f,
0.162f, 0.951f, 0.000f, 0.295f, 0.955f, 0.000f, 0.000f, 1.000f,
-0.717f, -0.148f, 0.682f, -0.526f, 0.000f, 0.851f, -0.500f, -0.309f,
0.809f, 0.000f, -0.295f, 0.955f, -0.263f, -0.162f, 0.951f, -0.239f,
-0.443f, 0.864f, 0.000f, -0.526f, 0.851f, -0.951f, -0.263f, 0.162f,
-0.864f, -0.239f, 0.443f, -0.809f, -0.500f, 0.309f, -0.864f, -0.239f,
-0.443f, -0.951f, -0.263f, -0.162f, -0.809f, -0.500f, -0.309f, -0.682f,
-0.717f, 0.148f, -0.851f, -0.526f, 0.000f, -0.682f, -0.717f, -0.148f,
-0.526f, -0.851f, 0.000f, -0.526f, 0.000f, -0.851f, -0.717f, -0.148f,
-0.682f, -0.500f, -0.309f, -0.809f, 0.000f, 0.295f, -0.955f, -0.263f,
0.162f, -0.951f, 0.000f, 0.000f, -1.000f, -0.239f, -0.443f, -0.864f,
-0.263f, -0.162f, -0.951f, 0.000f, -0.295f, -0.955f, 0.000f, -0.526f,
-0.851f, 0.425f, 0.588f, -0.688f, 0.239f, 0.443f, -0.864f, 0.500f,
0.309f, -0.809f, 0.682f, 0.717f, -0.148f, 0.588f, 0.688f, -0.425f,
0.809f, 0.500f, -0.309f, 0.717f, 0.148f, -0.682f, 0.688f, 0.425f,
-0.588f, 0.864f, 0.239f, -0.443f, 0.851f, 0.000f, -0.526f, 0.682f,
-0.717f, 0.148f, 0.588f, -0.688f, 0.425f, 0.443f, -0.864f, 0.239f,
0.526f, -0.851f, 0.000f, 0.809f, -0.500f, 0.309f, 0.309f, -0.809f,
0.500f, 0.717f, -0.148f, 0.682f, 0.688f, -0.425f, 0.588f, 0.864f,
-0.239f, 0.443f, 0.500f, -0.309f, 0.809f, 0.148f, -0.682f, 0.717f,
0.425f, -0.588f, 0.688f, 0.239f, -0.443f, 0.864f, 0.162f, -0.951f,
0.263f, 0.295f, -0.955f, 0.000f, 0.000f, -1.000f, 0.000f, -0.148f,
-0.682f, 0.717f, 0.000f, -0.851f, 0.526f, -0.309f, -0.809f, 0.500f,
-0.295f, -0.955f, 0.000f, -0.162f, -0.951f, 0.263f, -0.443f, -0.864f,
0.239f, 0.162f, -0.951f, -0.263f, 0.443f, -0.864f, -0.239f, 0.309f,
-0.809f, -0.500f, -0.443f, -0.864f, -0.239f, -0.162f, -0.951f, -0.263f,
-0.309f, -0.809f, -0.500f, 0.148f, -0.682f, -0.717f, 0.000f, -0.851f,
-0.526f, -0.148f, -0.682f, -0.717f, 0.588f, -0.688f, -0.425f, 0.682f,
-0.717f, -0.148f, 0.809f, -0.500f, -0.309f, 0.239f, -0.443f, -0.864f,
0.425f, -0.588f, -0.688f, 0.500f, -0.309f, -0.809f, 0.864f, -0.239f,
-0.443f, 0.688f, -0.425f, -0.588f, 0.717f, -0.148f, -0.682f, 0.851f,
-0.526f, 0.000f, 0.955f, 0.000f, -0.295f, 0.951f, -0.263f, -0.162f,
1.000f, 0.000f, 0.000f, 0.951f, -0.263f, 0.162f, 0.955f, 0.000f,
0.295f, 0.263f, -0.162f, 0.951f, 0.526f, 0.000f, 0.851f, 0.263f,
0.162f, 0.951f, -0.588f, -0.688f, 0.425f, -0.425f, -0.588f, 0.688f,
-0.688f, -0.425f, 0.588f, -0.425f, -0.588f, -0.688f, -0.588f, -0.688f,
-0.425f, -0.688f, -0.425f, -0.588f, 0.526f, 0.000f, -0.851f, 0.263f,
-0.162f, -0.951f, 0.263f, 0.162f, -0.951f, 0.951f, 0.263f, 0.162f,
0.951f, 0.263f, -0.162f, 0.851f, 0.526f, 0.000f};
static const int kNumVertices = OZZ_ARRAY_SIZE(kVertices);
static const uint16_t kIndices[] = {
3, 0, 2, 4, 1, 0, 5, 2, 1, 0, 1, 2, 9, 6, 8,
10, 7, 6, 4, 8, 7, 6, 7, 8, 14, 11, 13, 5, 12, 11,
10, 13, 12, 11, 12, 13, 4, 7, 1, 10, 12, 7, 5, 1, 12,
7, 12, 1, 3, 2, 16, 5, 15, 2, 17, 16, 15, 2, 15, 16,
14, 18, 11, 20, 19, 18, 5, 11, 19, 18, 19, 11, 24, 21, 23,
17, 22, 21, 20, 23, 22, 21, 22, 23, 5, 19, 15, 20, 22, 19,
17, 15, 22, 19, 22, 15, 3, 16, 26, 17, 25, 16, 27, 26, 25,
16, 25, 26, 24, 28, 21, 30, 29, 28, 17, 21, 29, 28, 29, 21,
34, 31, 33, 27, 32, 31, 30, 33, 32, 31, 32, 33, 17, 29, 25,
30, 32, 29, 27, 25, 32, 29, 32, 25, 3, 26, 36, 27, 35, 26,
37, 36, 35, 26, 35, 36, 34, 38, 31, 40, 39, 38, 27, 31, 39,
38, 39, 31, 44, 41, 43, 37, 42, 41, 40, 43, 42, 41, 42, 43,
27, 39, 35, 40, 42, 39, 37, 35, 42, 39, 42, 35, 3, 36, 0,
37, 45, 36, 4, 0, 45, 36, 45, 0, 44, 46, 41, 48, 47, 46,
37, 41, 47, 46, 47, 41, 9, 8, 50, 4, 49, 8, 48, 50, 49,
8, 49, 50, 37, 47, 45, 48, 49, 47, 4, 45, 49, 47, 49, 45,
24, 23, 52, 20, 51, 23, 53, 52, 51, 23, 51, 52, 14, 54, 18,
56, 55, 54, 20, 18, 55, 54, 55, 18, 60, 57, 59, 53, 58, 57,
56, 59, 58, 57, 58, 59, 20, 55, 51, 56, 58, 55, 53, 51, 58,
55, 58, 51, 14, 13, 62, 10, 61, 13, 63, 62, 61, 13, 61, 62,
9, 64, 6, 66, 65, 64, 10, 6, 65, 64, 65, 6, 70, 67, 69,
63, 68, 67, 66, 69, 68, 67, 68, 69, 10, 65, 61, 66, 68, 65,
63, 61, 68, 65, 68, 61, 9, 50, 72, 48, 71, 50, 73, 72, 71,
50, 71, 72, 44, 74, 46, 76, 75, 74, 48, 46, 75, 74, 75, 46,
80, 77, 79, 73, 78, 77, 76, 79, 78, 77, 78, 79, 48, 75, 71,
76, 78, 75, 73, 71, 78, 75, 78, 71, 44, 43, 82, 40, 81, 43,
83, 82, 81, 43, 81, 82, 34, 84, 38, 86, 85, 84, 40, 38, 85,
84, 85, 38, 90, 87, 89, 83, 88, 87, 86, 89, 88, 87, 88, 89,
40, 85, 81, 86, 88, 85, 83, 81, 88, 85, 88, 81, 34, 33, 92,
30, 91, 33, 93, 92, 91, 33, 91, 92, 24, 94, 28, 96, 95, 94,
30, 28, 95, 94, 95, 28, 100, 97, 99, 93, 98, 97, 96, 99, 98,
97, 98, 99, 30, 95, 91, 96, 98, 95, 93, 91, 98, 95, 98, 91,
104, 101, 103, 105, 102, 101, 106, 103, 102, 101, 102, 103, 60, 107, 109,
110, 108, 107, 105, 109, 108, 107, 108, 109, 70, 111, 113, 106, 112, 111,
110, 113, 112, 111, 112, 113, 105, 108, 102, 110, 112, 108, 106, 102, 112,
108, 112, 102, 104, 103, 115, 106, 114, 103, 116, 115, 114, 103, 114, 115,
70, 117, 111, 119, 118, 117, 106, 111, 118, 117, 118, 111, 80, 120, 122,
116, 121, 120, 119, 122, 121, 120, 121, 122, 106, 118, 114, 119, 121, 118,
116, 114, 121, 118, 121, 114, 104, 115, 124, 116, 123, 115, 125, 124, 123,
115, 123, 124, 80, 126, 120, 128, 127, 126, 116, 120, 127, 126, 127, 120,
90, 129, 131, 125, 130, 129, 128, 131, 130, 129, 130, 131, 116, 127, 123,
128, 130, 127, 125, 123, 130, 127, 130, 123, 104, 124, 133, 125, 132, 124,
134, 133, 132, 124, 132, 133, 90, 135, 129, 137, 136, 135, 125, 129, 136,
135, 136, 129, 100, 138, 140, 134, 139, 138, 137, 140, 139, 138, 139, 140,
125, 136, 132, 137, 139, 136, 134, 132, 139, 136, 139, 132, 104, 133, 101,
134, 141, 133, 105, 101, 141, 133, 141, 101, 100, 142, 138, 144, 143, 142,
134, 138, 143, 142, 143, 138, 60, 109, 146, 105, 145, 109, 144, 146, 145,
109, 145, 146, 134, 143, 141, 144, 145, 143, 105, 141, 145, 143, 145, 141,
70, 113, 67, 110, 147, 113, 63, 67, 147, 113, 147, 67, 60, 59, 107,
56, 148, 59, 110, 107, 148, 59, 148, 107, 14, 62, 54, 63, 149, 62,
56, 54, 149, 62, 149, 54, 110, 148, 147, 56, 149, 148, 63, 147, 149,
148, 149, 147, 80, 122, 77, 119, 150, 122, 73, 77, 150, 122, 150, 77,
70, 69, 117, 66, 151, 69, 119, 117, 151, 69, 151, 117, 9, 72, 64,
73, 152, 72, 66, 64, 152, 72, 152, 64, 119, 151, 150, 66, 152, 151,
73, 150, 152, 151, 152, 150, 90, 131, 87, 128, 153, 131, 83, 87, 153,
131, 153, 87, 80, 79, 126, 76, 154, 79, 128, 126, 154, 79, 154, 126,
44, 82, 74, 83, 155, 82, 76, 74, 155, 82, 155, 74, 128, 154, 153,
76, 155, 154, 83, 153, 155, 154, 155, 153, 100, 140, 97, 137, 156, 140,
93, 97, 156, 140, 156, 97, 90, 89, 135, 86, 157, 89, 137, 135, 157,
89, 157, 135, 34, 92, 84, 93, 158, 92, 86, 84, 158, 92, 158, 84,
137, 157, 156, 86, 158, 157, 93, 156, 158, 157, 158, 156, 60, 146, 57,
144, 159, 146, 53, 57, 159, 146, 159, 57, 100, 99, 142, 96, 160, 99,
144, 142, 160, 99, 160, 142, 24, 52, 94, 53, 161, 52, 96, 94, 161,
52, 161, 94, 144, 160, 159, 96, 161, 160, 53, 159, 161, 160, 161, 159};
static const int kNumIndices = OZZ_ARRAY_SIZE(kIndices);
} // namespace icosphere
#endif // OZZ_SAMPLES_FRAMEWORK_INTERNAL_ICOSPHERE_H_
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,233 @@
//----------------------------------------------------------------------------//
// //
// ozz-animation is hosted at http://github.com/guillaumeblanc/ozz-animation //
// and distributed under the MIT License (MIT). //
// //
// Copyright (c) Guillaume Blanc //
// //
// Permission is hereby granted, free of charge, to any person obtaining a //
// copy of this software and associated documentation files (the "Software"), //
// to deal in the Software without restriction, including without limitation //
// the rights to use, copy, modify, merge, publish, distribute, sublicense, //
// and/or sell copies of the Software, and to permit persons to whom the //
// Software is furnished to do so, subject to the following conditions: //
// //
// The above copyright notice and this permission notice shall be included in //
// all copies or substantial portions of the Software. //
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL //
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING //
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER //
// DEALINGS IN THE SOFTWARE. //
// //
//----------------------------------------------------------------------------//
#ifndef OZZ_SAMPLES_FRAMEWORK_INTERNAL_IMGUI_IMPL_H_
#define OZZ_SAMPLES_FRAMEWORK_INTERNAL_IMGUI_IMPL_H_
#ifndef OZZ_INCLUDE_PRIVATE_HEADER
#error "This header is private, it cannot be included from public headers."
#endif // OZZ_INCLUDE_PRIVATE_HEADER
// Implements immediate mode gui.
// See imgui.h for details about function specifications.
#include "framework/imgui.h"
#include "ozz/base/containers/vector.h"
#include "ozz/base/maths/rect.h"
#include "renderer_impl.h"
namespace ozz {
namespace sample {
namespace internal {
class RendererImpl;
// Immediate mode gui implementation.
class ImGuiImpl : public ImGui {
public:
ImGuiImpl();
virtual ~ImGuiImpl();
// Input state.
struct Inputs {
// Default input values.
Inputs() : mouse_x(0), mouse_y(0), lmb_pressed(false) {}
// Cursor x position. 0 indicates the screen left border.
int mouse_x;
// Cursor y position. 0 indicates the screen bottom border.
int mouse_y;
// Left mouse button state. true when the left mouse button is pressed.
bool lmb_pressed;
};
// Starts an imgui frame.
// _inputs describes next frame inputs. It is internally copied.
// _rect is the windows size.
void BeginFrame(const Inputs& _inputs, const math::RectInt& _rect,
RendererImpl* _renderer);
// Ends the current imgui frame.
void EndFrame();
protected:
// Starts ImGui interface implementation.
// See imgui.h for virtual function specifications.
virtual void BeginContainer(const char* _title, const math::RectFloat* _rect,
bool* _open, bool _constrain);
virtual void EndContainer();
virtual bool DoButton(const char* _label, bool _enabled, bool* _state);
virtual bool DoSlider(const char* _label, float _min, float _max,
float* _value, float _pow, bool _enabled);
virtual bool DoSlider(const char* _label, int _min, int _max, int* _value,
float _pow, bool _enabled);
virtual bool DoCheckBox(const char* _label, bool* _state, bool _enabled);
virtual bool DoRadioButton(int _ref, const char* _label, int* _value,
bool _enabled);
virtual void DoLabel(const char* _label, Justification _justification,
bool _single_line);
virtual void DoGraph(const char* _label, float _min, float _max, float _mean,
const float* _value_cursor, const float* _value_begin,
const float* _value_end);
private:
// Computes the rect of a new widget to add.
// Returns true if there is enough space for a new widget.
bool AddWidget(float _height, math::RectFloat* _rect);
// Implements button logic.
// Returns true if the button was clicked.
bool ButtonLogic(const math::RectFloat& _rect, int _id, bool* _hot,
bool* _active);
// Fills a rectangle with _rect coordinates. Draws rounded angles if _radius
// is greater than 0.
// If _texture id is not 0, then texture with id _texture is mapped using a
// planar projection to the rect.
void FillRect(const math::RectFloat& _rect, float _radius,
const GLubyte _rgba[4]) const;
void FillRect(const math::RectFloat& _rect, float _radius,
const GLubyte _rgba[4],
const ozz::math::Float4x4& _transform) const;
// Strokes a rectangle with _rect coordinates. Draws rounded angles if _radius
// is greater than 0.
void StrokeRect(const math::RectFloat& _rect, float _radius,
const GLubyte _rgba[4]) const;
void StrokeRect(const math::RectFloat& _rect, float _radius,
const GLubyte _rgba[4],
const ozz::math::Float4x4& _transform) const;
enum PrintLayout {
kNorthWest,
kNorth,
kNorthEst,
kWest,
kMiddle,
kEst,
kSouthWest,
kSouth,
kSouthEst,
};
// Print _text in _rect.
float Print(const char* _text, const math::RectFloat& _rect,
PrintLayout _layout, const GLubyte _rgba[4]) const;
// Initialize circle vertices.
void InitializeCircle();
// Initializes and destroys the internal font rendering system.
void InitalizeFont();
void DestroyFont();
// ImGui state.
// Current frame inputs.
Inputs inputs_;
// Internal states.
// The hot item is the one that's below the mouse cursor.
int hot_item_;
// The active item is the one being currently interacted with.
int active_item_;
// The automatically generated widget identifier.
int auto_gen_id_;
struct Container {
// The current rect.
math::RectFloat rect;
// The y offset of the top of the next widget in the current container.
float offset_y;
// Constrains container height to the size used by controls in the
// container.
bool constrain;
};
// Container stack.
ozz::vector<Container> containers_;
// Round rendering.
// Defines the number of segments used by the precomputed circle.
// Must be multiple of 4.
static const int kCircleSegments = 8;
// Circle vertices coordinates (cosine/sinus)
float circle_[kCircleSegments][2];
// Font rendering.
// Declares font's structure.
struct Font {
int texture_width; // width of the pow2 texture.
int texture_height; // height of the pow2 texture.
int image_width; // width of the image area in the texture.
int image_height; // height of the image area in the texture.
int glyph_height;
int glyph_width;
int glyph_count;
unsigned char glyph_start; // ascii code of the first character.
const unsigned char* pixels;
size_t pixels_size;
};
// Declares the font instance.
static const Font font_;
// Pre-cooked glyphes uv and vertex coordinates.
struct Glyph {
float uv[4][2];
float pos[4][2];
};
Glyph glyphes_[256];
// Glyph GL texture
unsigned int glyph_texture_;
// Renderer, available between Begin/EndFrame
RendererImpl* renderer_;
};
} // namespace internal
} // namespace sample
} // namespace ozz
#endif // OZZ_SAMPLES_FRAMEWORK_INTERNAL_IMGUI_IMPL_H_
@@ -0,0 +1,116 @@
//----------------------------------------------------------------------------//
// //
// ozz-animation is hosted at http://github.com/guillaumeblanc/ozz-animation //
// and distributed under the MIT License (MIT). //
// //
// Copyright (c) Guillaume Blanc //
// //
// Permission is hereby granted, free of charge, to any person obtaining a //
// copy of this software and associated documentation files (the "Software"), //
// to deal in the Software without restriction, including without limitation //
// the rights to use, copy, modify, merge, publish, distribute, sublicense, //
// and/or sell copies of the Software, and to permit persons to whom the //
// Software is furnished to do so, subject to the following conditions: //
// //
// The above copyright notice and this permission notice shall be included in //
// all copies or substantial portions of the Software. //
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL //
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING //
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER //
// DEALINGS IN THE SOFTWARE. //
// //
//----------------------------------------------------------------------------//
#define OZZ_INCLUDE_PRIVATE_HEADER // Allows to include private headers.
#include "immediate.h"
#include <cassert>
#include "camera.h"
#include "ozz/base/memory/allocator.h"
#include "renderer_impl.h"
#include "shader.h"
namespace ozz {
namespace sample {
namespace internal {
GlImmediateRenderer::GlImmediateRenderer(RendererImpl* _renderer)
: vbo_(0), size_(0), renderer_(_renderer) {}
GlImmediateRenderer::~GlImmediateRenderer() {
assert(size_ == 0 && "Immediate rendering still in use.");
if (vbo_) {
GL(DeleteBuffers(1, &vbo_));
vbo_ = 0;
}
}
bool GlImmediateRenderer::Initialize() {
GL(GenBuffers(1, &vbo_));
immediate_pc_shader = ImmediatePCShader::Build();
if (!immediate_pc_shader) {
return false;
}
immediate_ptc_shader = ImmediatePTCShader::Build();
if (!immediate_ptc_shader) {
return false;
}
return true;
}
void GlImmediateRenderer::Begin() {
assert(size_ == 0 && "Immediate rendering already in use.");
}
template <>
void GlImmediateRenderer::End<VertexPC>(GLenum _mode,
const ozz::math::Float4x4& _transform) {
GL(BindBuffer(GL_ARRAY_BUFFER, vbo_));
GL(BufferData(GL_ARRAY_BUFFER, size_, buffer_.data(), GL_STREAM_DRAW));
immediate_pc_shader->Bind(_transform, renderer_->camera()->view_proj(),
sizeof(VertexPC), 0, sizeof(VertexPC), 12);
const int count = static_cast<int>(size_ / sizeof(VertexPC));
GL(DrawArrays(_mode, 0, count));
immediate_pc_shader->Unbind();
GL(BindBuffer(GL_ARRAY_BUFFER, 0));
// Reset vertex count for the next call
size_ = 0;
}
template <>
void GlImmediateRenderer::End<VertexPTC>(
GLenum _mode, const ozz::math::Float4x4& _transform) {
GL(BindBuffer(GL_ARRAY_BUFFER, vbo_));
GL(BufferData(GL_ARRAY_BUFFER, size_, buffer_.data(), GL_STREAM_DRAW));
immediate_ptc_shader->Bind(_transform, renderer_->camera()->view_proj(),
sizeof(VertexPTC), 0, sizeof(VertexPTC), 12,
sizeof(VertexPTC), 20);
const int count = static_cast<int>(size_ / sizeof(VertexPTC));
GL(DrawArrays(_mode, 0, count));
immediate_ptc_shader->Unbind();
GL(BindBuffer(GL_ARRAY_BUFFER, 0));
// Reset vertex count for the next call
size_ = 0;
}
} // namespace internal
} // namespace sample
} // namespace ozz
@@ -0,0 +1,173 @@
//----------------------------------------------------------------------------//
// //
// ozz-animation is hosted at http://github.com/guillaumeblanc/ozz-animation //
// and distributed under the MIT License (MIT). //
// //
// Copyright (c) Guillaume Blanc //
// //
// Permission is hereby granted, free of charge, to any person obtaining a //
// copy of this software and associated documentation files (the "Software"), //
// to deal in the Software without restriction, including without limitation //
// the rights to use, copy, modify, merge, publish, distribute, sublicense, //
// and/or sell copies of the Software, and to permit persons to whom the //
// Software is furnished to do so, subject to the following conditions: //
// //
// The above copyright notice and this permission notice shall be included in //
// all copies or substantial portions of the Software. //
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL //
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING //
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER //
// DEALINGS IN THE SOFTWARE. //
// //
//----------------------------------------------------------------------------//
#ifndef OZZ_SAMPLES_FRAMEWORK_INTERNAL_IMMEDIATE_H_
#define OZZ_SAMPLES_FRAMEWORK_INTERNAL_IMMEDIATE_H_
#ifndef OZZ_INCLUDE_PRIVATE_HEADER
#error "This header is private, it cannot be included from public headers."
#endif // OZZ_INCLUDE_PRIVATE_HEADER
#include <cstring>
#include "ozz/base/containers/vector.h"
#include "ozz/base/maths/math_ex.h"
#include "ozz/base/maths/simd_math.h"
#include "ozz/base/memory/unique_ptr.h"
#include "ozz/base/platform.h"
#include "renderer_impl.h"
namespace ozz {
namespace sample {
namespace internal {
class RendererImpl;
class ImmediatePCShader;
class ImmediatePNShader;
class ImmediatePTCShader;
// Declares supported vertex formats.
// Position + Color.
struct VertexPC {
float pos[3];
GLubyte rgba[4];
};
// Declares supported vertex formats.
// Position + Normal.
struct VertexPN {
float pos[3];
float normal[3];
};
// Position + Texture coordinate + Color.
struct VertexPTC {
float pos[3];
float uv[2];
GLubyte rgba[4];
};
// Declares Immediate mode types.
template <typename _Ty>
class GlImmediate;
typedef GlImmediate<VertexPC> GlImmediatePC;
typedef GlImmediate<VertexPN> GlImmediatePN;
typedef GlImmediate<VertexPTC> GlImmediatePTC;
// GL immedialte mode renderer.
// Should be used with a GlImmediate object to push vertices to the renderer.
class GlImmediateRenderer {
public:
GlImmediateRenderer(RendererImpl* _renderer);
~GlImmediateRenderer();
// Initializes immediate mode renderer. Can fail.
bool Initialize();
private:
// GlImmediate is used to work with the renderer.
template <typename _Ty>
friend class GlImmediate;
// Begin stacking vertices.
void Begin();
// End stacking vertices. Call GL rendering.
template <typename _Ty>
void End(GLenum _mode, const ozz::math::Float4x4& _transform);
// Push a new vertex to the buffer.
template <typename _Ty>
OZZ_INLINE void PushVertex(const _Ty& _vertex) {
// Resize buffer if needed.
const size_t new_size = size_ + sizeof(_Ty);
if (new_size > buffer_.size()) {
buffer_.resize(new_size);
}
// Copy this last vertex.
std::memcpy(buffer_.data() + size_, &_vertex, sizeof(_Ty));
size_ = new_size;
}
// The vertex object used by the renderer.
GLuint vbo_;
// Buffer of vertices.
ozz::vector<char> buffer_;
// Number of vertices.
size_t size_;
// Immediate mode shaders;
ozz::unique_ptr<ImmediatePCShader> immediate_pc_shader;
ozz::unique_ptr<ImmediatePTCShader> immediate_ptc_shader;
// The renderer object.
RendererImpl* renderer_;
};
// RAII object that allows to push vertices to the imrender stack.
template <typename _Ty>
class GlImmediate {
public:
// Immediate object vertex format.
typedef _Ty Vertex;
// Start a new immediate stack.
GlImmediate(GlImmediateRenderer* _renderer, GLenum _mode,
const ozz::math::Float4x4& _transform)
: transform_(_transform), renderer_(_renderer), mode_(_mode) {
renderer_->Begin();
}
// End immediate vertex stacking, and renders all vertices.
~GlImmediate() { renderer_->End<_Ty>(mode_, transform_); }
// Pushes a new vertex to the stack.
OZZ_INLINE void PushVertex(const _Ty& _vertex) {
renderer_->PushVertex(_vertex);
}
private:
// Non copyable.
GlImmediate(const GlImmediate&);
void operator=(const GlImmediate&);
// Transformation matrix.
const ozz::math::Float4x4 transform_;
// Shared renderer.
GlImmediateRenderer* renderer_;
// Draw array mode GL_POINTS, GL_LINE_STRIP, ...
GLenum mode_;
};
} // namespace internal
} // namespace sample
} // namespace ozz
#endif // OZZ_SAMPLES_FRAMEWORK_INTERNAL_IMMEDIATE_H_
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,332 @@
//----------------------------------------------------------------------------//
// //
// ozz-animation is hosted at http://github.com/guillaumeblanc/ozz-animation //
// and distributed under the MIT License (MIT). //
// //
// Copyright (c) Guillaume Blanc //
// //
// Permission is hereby granted, free of charge, to any person obtaining a //
// copy of this software and associated documentation files (the "Software"), //
// to deal in the Software without restriction, including without limitation //
// the rights to use, copy, modify, merge, publish, distribute, sublicense, //
// and/or sell copies of the Software, and to permit persons to whom the //
// Software is furnished to do so, subject to the following conditions: //
// //
// The above copyright notice and this permission notice shall be included in //
// all copies or substantial portions of the Software. //
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL //
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING //
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER //
// DEALINGS IN THE SOFTWARE. //
// //
//----------------------------------------------------------------------------//
#ifndef OZZ_SAMPLES_FRAMEWORK_INTERNAL_RENDERER_IMPL_H_
#define OZZ_SAMPLES_FRAMEWORK_INTERNAL_RENDERER_IMPL_H_
#ifndef OZZ_INCLUDE_PRIVATE_HEADER
#error "This header is private, it cannot be included from public headers."
#endif // OZZ_INCLUDE_PRIVATE_HEADER
// GL and GL ext requires that ptrdif_t is defined on APPLE platforms.
#include <cstddef>
// Don't allow gl.h to automatically include glext.h
#define GL_GLEXT_LEGACY
// Including glfw includes gl.h
#include "GL/glfw.h"
#ifdef EMSCRIPTEN
// include features as core functions.
#include <GLES2/gl2.h>
#else // EMSCRIPTEN
// Detects already defined GL_VERSION and deduces required extensions.
#ifndef GL_VERSION_1_5
#define OZZ_GL_VERSION_1_5_EXT
#endif // GL_VERSION_1_5
#ifndef GL_VERSION_2_0
#define OZZ_GL_VERSION_2_0_EXT
#endif // GL_VERSION_2_0
#endif // EMSCRIPTEN
// Include features as extentions
#include "GL/glext.h"
#include "framework/renderer.h"
#include "ozz/base/containers/vector.h"
#include "ozz/base/memory/unique_ptr.h"
// Provides helper macro to test for glGetError on a gl call.
#ifndef NDEBUG
#define GL(_f) \
do { \
gl##_f; \
GLenum error = glGetError(); \
assert(error == GL_NO_ERROR); \
\
} while (void(0), 0)
#else // NDEBUG
#define GL(_f) gl##_f
#endif // NDEBUG
// Convenient macro definition for specifying buffer offsets.
#define GL_PTR_OFFSET(i) reinterpret_cast<void*>(static_cast<intptr_t>(i))
namespace ozz {
namespace animation {
class Skeleton;
}
namespace math {
struct Float4x4;
}
namespace sample {
namespace internal {
class Camera;
class Shader;
class SkeletonShader;
class AmbientShader;
class AmbientTexturedShader;
class AmbientShaderInstanced;
class GlImmediateRenderer;
// Implements Renderer interface.
class RendererImpl : public Renderer {
public:
RendererImpl(Camera* _camera);
virtual ~RendererImpl();
// See Renderer for all the details about the API.
virtual bool Initialize();
virtual bool DrawAxes(const ozz::math::Float4x4& _transform);
virtual bool DrawGrid(int _cell_count, float _cell_size);
virtual bool DrawSkeleton(const animation::Skeleton& _skeleton,
const ozz::math::Float4x4& _transform,
bool _draw_joints);
virtual bool DrawPosture(const animation::Skeleton& _skeleton,
ozz::span<const ozz::math::Float4x4> _matrices,
const ozz::math::Float4x4& _transform,
bool _draw_joints);
virtual bool DrawBoxIm(const ozz::math::Box& _box,
const ozz::math::Float4x4& _transform,
const Color _colors[2]);
virtual bool DrawBoxShaded(const ozz::math::Box& _box,
ozz::span<const ozz::math::Float4x4> _transforms,
Color _color);
virtual bool DrawSphereIm(float _radius,
const ozz::math::Float4x4& _transform,
const Color _color);
virtual bool DrawSphereShaded(
float _radius, ozz::span<const ozz::math::Float4x4> _transforms,
Color _color);
virtual bool DrawSkinnedMesh(const Mesh& _mesh,
const span<math::Float4x4> _skinning_matrices,
const ozz::math::Float4x4& _transform,
const Options& _options = Options());
virtual bool DrawMesh(const Mesh& _mesh,
const ozz::math::Float4x4& _transform,
const Options& _options = Options());
virtual bool DrawSegment(const math::Float3& _begin, const math::Float3& _end,
Color _color, const ozz::math::Float4x4& _transform);
virtual bool DrawVectors(ozz::span<const float> _positions,
size_t _positions_stride,
ozz::span<const float> _directions,
size_t _directions_stride, int _num_vectors,
float _vector_length, Color _color,
const ozz::math::Float4x4& _transform);
virtual bool DrawBinormals(
ozz::span<const float> _positions, size_t _positions_stride,
ozz::span<const float> _normals, size_t _normals_stride,
ozz::span<const float> _tangents, size_t _tangents_stride,
ozz::span<const float> _handenesses, size_t _handenesses_stride,
int _num_vectors, float _vector_length, Color _color,
const ozz::math::Float4x4& _transform);
// Get GL immediate renderer implementation;
GlImmediateRenderer* immediate_renderer() const { return immediate_.get(); }
// Get application camera that provides rendering matrices.
Camera* camera() const { return camera_; }
private:
// Defines the internal structure used to define a model.
struct Model {
Model();
~Model();
GLuint vbo;
GLenum mode;
GLsizei count;
ozz::unique_ptr<SkeletonShader> shader;
};
// Detects and initializes all OpenGL extension.
// Return true if all mandatory extensions were found.
bool InitOpenGLExtensions();
// Initializes posture rendering.
// Return true if initialization succeeded.
bool InitPostureRendering();
// Initializes the checkered texture.
// Return true if initialization succeeded.
bool InitCheckeredTexture();
// Draw posture internal non-instanced rendering fall back implementation.
void DrawPosture_Impl(const ozz::math::Float4x4& _transform,
const float* _uniforms, int _instance_count,
bool _draw_joints);
// Draw posture internal instanced rendering implementation.
void DrawPosture_InstancedImpl(const ozz::math::Float4x4& _transform,
const float* _uniforms, int _instance_count,
bool _draw_joints);
// Array of matrices used to store model space matrices during DrawSkeleton
// execution.
ozz::vector<ozz::math::Float4x4> prealloc_models_;
// Application camera that provides rendering matrices.
Camera* camera_;
// Bone and joint model objects.
Model models_[2];
// Dynamic vbo used for arrays.
GLuint dynamic_array_bo_;
// Dynamic vbo used for indices.
GLuint dynamic_index_bo_;
// Volatile memory buffer that can be used within function scope.
// Minimum alignment is 16 bytes.
class ScratchBuffer {
public:
ScratchBuffer();
~ScratchBuffer();
// Resizes the buffer to the new size and return the memory address.
void* Resize(size_t _size);
private:
void* buffer_;
size_t size_;
};
ScratchBuffer scratch_buffer_;
// Immediate renderer implementation.
ozz::unique_ptr<GlImmediateRenderer> immediate_;
// Ambient rendering shader.
ozz::unique_ptr<AmbientShader> ambient_shader;
ozz::unique_ptr<AmbientTexturedShader> ambient_textured_shader;
ozz::unique_ptr<AmbientShaderInstanced> ambient_shader_instanced;
// Checkered texture
unsigned int checkered_texture_;
};
} // namespace internal
} // namespace sample
} // namespace ozz
// OpenGL 1.5 buffer object management functions, mandatory.
#ifdef OZZ_GL_VERSION_1_5_EXT
extern PFNGLBINDBUFFERPROC glBindBuffer;
extern PFNGLDELETEBUFFERSPROC glDeleteBuffers;
extern PFNGLGENBUFFERSPROC glGenBuffers;
extern PFNGLISBUFFERPROC glIsBuffer;
extern PFNGLBUFFERDATAPROC glBufferData;
extern PFNGLBUFFERSUBDATAPROC glBufferSubData;
extern PFNGLGETBUFFERSUBDATAPROC glGetBufferSubData;
extern PFNGLMAPBUFFERPROC glMapBuffer;
extern PFNGLUNMAPBUFFERPROC glUnmapBuffer;
extern PFNGLGETBUFFERPARAMETERIVPROC glGetBufferParameteriv;
extern PFNGLGETBUFFERPOINTERVPROC glGetBufferPointerv;
#endif // OZZ_GL_VERSION_1_5_EXT
// OpenGL 2.0 shader management functions, mandatory.
#ifdef OZZ_GL_VERSION_2_0_EXT
extern PFNGLATTACHSHADERPROC glAttachShader;
extern PFNGLBINDATTRIBLOCATIONPROC glBindAttribLocation;
extern PFNGLCOMPILESHADERPROC glCompileShader;
extern PFNGLCREATEPROGRAMPROC glCreateProgram;
extern PFNGLCREATESHADERPROC glCreateShader;
extern PFNGLDELETEPROGRAMPROC glDeleteProgram;
extern PFNGLDELETESHADERPROC glDeleteShader;
extern PFNGLDETACHSHADERPROC glDetachShader;
extern PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray;
extern PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray;
extern PFNGLGETACTIVEATTRIBPROC glGetActiveAttrib;
extern PFNGLGETACTIVEUNIFORMPROC glGetActiveUniform;
extern PFNGLGETATTACHEDSHADERSPROC glGetAttachedShaders;
extern PFNGLGETATTRIBLOCATIONPROC glGetAttribLocation;
extern PFNGLGETPROGRAMIVPROC glGetProgramiv;
extern PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog;
extern PFNGLGETSHADERIVPROC glGetShaderiv;
extern PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog;
extern PFNGLGETSHADERSOURCEPROC glGetShaderSource;
extern PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation;
extern PFNGLGETUNIFORMFVPROC glGetUniformfv;
extern PFNGLGETUNIFORMIVPROC glGetUniformiv;
extern PFNGLGETVERTEXATTRIBDVPROC glGetVertexAttribdv;
extern PFNGLGETVERTEXATTRIBFVPROC glGetVertexAttribfv;
extern PFNGLGETVERTEXATTRIBIVPROC glGetVertexAttribiv;
extern PFNGLGETVERTEXATTRIBPOINTERVPROC glGetVertexAttribPointerv;
extern PFNGLISPROGRAMPROC glIsProgram;
extern PFNGLISSHADERPROC glIsShader;
extern PFNGLLINKPROGRAMPROC glLinkProgram;
extern PFNGLSHADERSOURCEPROC glShaderSource;
extern PFNGLUSEPROGRAMPROC glUseProgram;
extern PFNGLUNIFORM1FPROC glUniform1f;
extern PFNGLUNIFORM2FPROC glUniform2f;
extern PFNGLUNIFORM3FPROC glUniform3f;
extern PFNGLUNIFORM4FPROC glUniform4f;
extern PFNGLUNIFORM1IPROC glUniform1i;
extern PFNGLUNIFORM2IPROC glUniform2i;
extern PFNGLUNIFORM3IPROC glUniform3i;
extern PFNGLUNIFORM4IPROC glUniform4i;
extern PFNGLUNIFORM1FVPROC glUniform1fv;
extern PFNGLUNIFORM2FVPROC glUniform2fv;
extern PFNGLUNIFORM3FVPROC glUniform3fv;
extern PFNGLUNIFORM4FVPROC glUniform4fv;
extern PFNGLUNIFORMMATRIX2FVPROC glUniformMatrix2fv;
extern PFNGLUNIFORMMATRIX3FVPROC glUniformMatrix3fv;
extern PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv;
extern PFNGLVALIDATEPROGRAMPROC glValidateProgram;
extern PFNGLVERTEXATTRIB1FPROC glVertexAttrib1f;
extern PFNGLVERTEXATTRIB1FVPROC glVertexAttrib1fv;
extern PFNGLVERTEXATTRIB2FPROC glVertexAttrib2f;
extern PFNGLVERTEXATTRIB2FVPROC glVertexAttrib2fv;
extern PFNGLVERTEXATTRIB3FPROC glVertexAttrib3f;
extern PFNGLVERTEXATTRIB3FVPROC glVertexAttrib3fv;
extern PFNGLVERTEXATTRIB4FPROC glVertexAttrib4f;
extern PFNGLVERTEXATTRIB4FVPROC glVertexAttrib4fv;
extern PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer;
#endif // OZZ_GL_VERSION_2_0_EXT
// OpenGL ARB_instanced_arrays extension, optional.
extern bool GL_ARB_instanced_arrays_supported;
extern PFNGLVERTEXATTRIBDIVISORARBPROC glVertexAttribDivisor_;
extern PFNGLDRAWARRAYSINSTANCEDARBPROC glDrawArraysInstanced_;
extern PFNGLDRAWELEMENTSINSTANCEDARBPROC glDrawElementsInstanced_;
#endif // OZZ_SAMPLES_FRAMEWORK_INTERNAL_RENDERER_IMPL_H_
@@ -0,0 +1,766 @@
//----------------------------------------------------------------------------//
// //
// ozz-animation is hosted at http://github.com/guillaumeblanc/ozz-animation //
// and distributed under the MIT License (MIT). //
// //
// Copyright (c) Guillaume Blanc //
// //
// Permission is hereby granted, free of charge, to any person obtaining a //
// copy of this software and associated documentation files (the "Software"), //
// to deal in the Software without restriction, including without limitation //
// the rights to use, copy, modify, merge, publish, distribute, sublicense, //
// and/or sell copies of the Software, and to permit persons to whom the //
// Software is furnished to do so, subject to the following conditions: //
// //
// The above copyright notice and this permission notice shall be included in //
// all copies or substantial portions of the Software. //
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL //
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING //
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER //
// DEALINGS IN THE SOFTWARE. //
// //
//----------------------------------------------------------------------------//
#define OZZ_INCLUDE_PRIVATE_HEADER // Allows to include private headers.
#include "shader.h"
#include <cassert>
#include <cstdio>
#include <limits>
#include "ozz/base/log.h"
#include "ozz/base/maths/simd_math.h"
namespace ozz {
namespace sample {
namespace internal {
#ifdef EMSCRIPTEN
// WebGL requires to specify floating point precision
static const char* kPlatformSpecivicVSHeader = "precision mediump float;\n";
static const char* kPlatformSpecivicFSHeader = "precision mediump float;\n";
#else // EMSCRIPTEN
static const char* kPlatformSpecivicVSHeader = "";
static const char* kPlatformSpecivicFSHeader = "";
#endif // EMSCRIPTEN
Shader::Shader() : program_(0), vertex_(0), fragment_(0) {}
Shader::~Shader() {
if (vertex_) {
GL(DetachShader(program_, vertex_));
GL(DeleteShader(vertex_));
}
if (fragment_) {
GL(DetachShader(program_, fragment_));
GL(DeleteShader(fragment_));
}
if (program_) {
GL(DeleteProgram(program_));
}
}
namespace {
GLuint CompileShader(GLenum _type, int _count, const char** _src) {
GLuint shader = glCreateShader(_type);
GL(ShaderSource(shader, _count, _src, nullptr));
GL(CompileShader(shader));
int infolog_length = 0;
GL(GetShaderiv(shader, GL_INFO_LOG_LENGTH, &infolog_length));
if (infolog_length > 1) {
char* info_log = reinterpret_cast<char*>(
memory::default_allocator()->Allocate(infolog_length, alignof(char)));
int chars_written = 0;
glGetShaderInfoLog(shader, infolog_length, &chars_written, info_log);
log::Err() << info_log << std::endl;
memory::default_allocator()->Deallocate(info_log);
}
int status;
GL(GetShaderiv(shader, GL_COMPILE_STATUS, &status));
if (status) {
return shader;
}
GL(DeleteShader(shader));
return 0;
}
} // namespace
bool Shader::BuildFromSource(int _vertex_count, const char** _vertex,
int _fragment_count, const char** _fragment) {
// Tries to compile shaders.
GLuint vertex_shader = 0;
if (_vertex) {
vertex_shader = CompileShader(GL_VERTEX_SHADER, _vertex_count, _vertex);
if (!vertex_shader) {
return false;
}
}
GLuint fragment_shader = 0;
if (_fragment) {
fragment_shader =
CompileShader(GL_FRAGMENT_SHADER, _fragment_count, _fragment);
if (!fragment_shader) {
if (vertex_shader) {
GL(DeleteShader(vertex_shader));
}
return false;
}
}
// Shaders are compiled, builds program.
program_ = glCreateProgram();
vertex_ = vertex_shader;
fragment_ = fragment_shader;
GL(AttachShader(program_, vertex_shader));
GL(AttachShader(program_, fragment_shader));
GL(LinkProgram(program_));
int infolog_length = 0;
GL(GetProgramiv(program_, GL_INFO_LOG_LENGTH, &infolog_length));
if (infolog_length > 1) {
char* info_log = reinterpret_cast<char*>(
memory::default_allocator()->Allocate(infolog_length, alignof(char)));
int chars_written = 0;
glGetProgramInfoLog(program_, infolog_length, &chars_written, info_log);
log::Err() << info_log << std::endl;
memory::default_allocator()->Deallocate(info_log);
}
return true;
}
bool Shader::BindUniform(const char* _semantic) {
if (!program_) {
return false;
}
GLint location = glGetUniformLocation(program_, _semantic);
if (glGetError() != GL_NO_ERROR || location == -1) { // _semantic not found.
return false;
}
uniforms_.push_back(location);
return true;
}
bool Shader::FindAttrib(const char* _semantic) {
if (!program_) {
return false;
}
GLint location = glGetAttribLocation(program_, _semantic);
if (glGetError() != GL_NO_ERROR || location == -1) { // _semantic not found.
return false;
}
attribs_.push_back(location);
return true;
}
void Shader::UnbindAttribs() {
for (size_t i = 0; i < attribs_.size(); ++i) {
GL(DisableVertexAttribArray(attribs_[i]));
}
}
void Shader::Unbind() {
UnbindAttribs();
GL(UseProgram(0));
}
ozz::unique_ptr<ImmediatePCShader> ImmediatePCShader::Build() {
bool success = true;
const char* kSimplePCVS =
"uniform mat4 u_mvp;\n"
"attribute vec3 a_position;\n"
"attribute vec4 a_color;\n"
"varying vec4 v_vertex_color;\n"
"void main() {\n"
" vec4 vertex = vec4(a_position.xyz, 1.);\n"
" gl_Position = u_mvp * vertex;\n"
" v_vertex_color = a_color;\n"
"}\n";
const char* kSimplePCPS =
"varying vec4 v_vertex_color;\n"
"void main() {\n"
" gl_FragColor = v_vertex_color;\n"
"}\n";
const char* vs[] = {kPlatformSpecivicVSHeader, kSimplePCVS};
const char* fs[] = {kPlatformSpecivicFSHeader, kSimplePCPS};
ozz::unique_ptr<ImmediatePCShader> shader = make_unique<ImmediatePCShader>();
success &=
shader->BuildFromSource(OZZ_ARRAY_SIZE(vs), vs, OZZ_ARRAY_SIZE(fs), fs);
// Binds default attributes
success &= shader->FindAttrib("a_position");
success &= shader->FindAttrib("a_color");
// Binds default uniforms
success &= shader->BindUniform("u_mvp");
if (!success) {
shader.reset();
}
return shader;
}
void ImmediatePCShader::Bind(const math::Float4x4& _model,
const math::Float4x4& _view_proj,
GLsizei _pos_stride, GLsizei _pos_offset,
GLsizei _color_stride, GLsizei _color_offset) {
GL(UseProgram(program()));
const GLint position_attrib = attrib(0);
GL(EnableVertexAttribArray(position_attrib));
GL(VertexAttribPointer(position_attrib, 3, GL_FLOAT, GL_FALSE, _pos_stride,
GL_PTR_OFFSET(_pos_offset)));
const GLint color_attrib = attrib(1);
GL(EnableVertexAttribArray(color_attrib));
GL(VertexAttribPointer(color_attrib, 4, GL_UNSIGNED_BYTE, GL_TRUE,
_color_stride, GL_PTR_OFFSET(_color_offset)));
// Binds mvp uniform
const GLint mvp_uniform = uniform(0);
const ozz::math::Float4x4 mvp = _view_proj * _model;
float values[16];
math::StorePtrU(mvp.cols[0], values + 0);
math::StorePtrU(mvp.cols[1], values + 4);
math::StorePtrU(mvp.cols[2], values + 8);
math::StorePtrU(mvp.cols[3], values + 12);
GL(UniformMatrix4fv(mvp_uniform, 1, false, values));
}
ozz::unique_ptr<ImmediatePTCShader> ImmediatePTCShader::Build() {
bool success = true;
const char* kSimplePCVS =
"uniform mat4 u_mvp;\n"
"attribute vec3 a_position;\n"
"attribute vec2 a_tex_coord;\n"
"attribute vec4 a_color;\n"
"varying vec4 v_vertex_color;\n"
"varying vec2 v_texture_coord;\n"
"void main() {\n"
" vec4 vertex = vec4(a_position.xyz, 1.);\n"
" gl_Position = u_mvp * vertex;\n"
" v_vertex_color = a_color;\n"
" v_texture_coord = a_tex_coord;\n"
"}\n";
const char* kSimplePCPS =
"uniform sampler2D u_texture;\n"
"varying vec4 v_vertex_color;\n"
"varying vec2 v_texture_coord;\n"
"void main() {\n"
" vec4 tex_color = texture2D(u_texture, v_texture_coord);\n"
" gl_FragColor = v_vertex_color * tex_color;\n"
" if(gl_FragColor.a < .01) discard;\n" // Implements alpha testing.
"}\n";
const char* vs[] = {kPlatformSpecivicVSHeader, kSimplePCVS};
const char* fs[] = {kPlatformSpecivicFSHeader, kSimplePCPS};
ozz::unique_ptr<ImmediatePTCShader> shader =
make_unique<ImmediatePTCShader>();
success &=
shader->BuildFromSource(OZZ_ARRAY_SIZE(vs), vs, OZZ_ARRAY_SIZE(fs), fs);
// Binds default attributes
success &= shader->FindAttrib("a_position");
success &= shader->FindAttrib("a_tex_coord");
success &= shader->FindAttrib("a_color");
// Binds default uniforms
success &= shader->BindUniform("u_mvp");
success &= shader->BindUniform("u_texture");
if (!success) {
shader.reset();
}
return shader;
}
void ImmediatePTCShader::Bind(const math::Float4x4& _model,
const math::Float4x4& _view_proj,
GLsizei _pos_stride, GLsizei _pos_offset,
GLsizei _tex_stride, GLsizei _tex_offset,
GLsizei _color_stride, GLsizei _color_offset) {
GL(UseProgram(program()));
const GLint position_attrib = attrib(0);
GL(EnableVertexAttribArray(position_attrib));
GL(VertexAttribPointer(position_attrib, 3, GL_FLOAT, GL_FALSE, _pos_stride,
GL_PTR_OFFSET(_pos_offset)));
const GLint tex_attrib = attrib(1);
GL(EnableVertexAttribArray(tex_attrib));
GL(VertexAttribPointer(tex_attrib, 2, GL_FLOAT, GL_FALSE, _tex_stride,
GL_PTR_OFFSET(_tex_offset)));
const GLint color_attrib = attrib(2);
GL(EnableVertexAttribArray(color_attrib));
GL(VertexAttribPointer(color_attrib, 4, GL_UNSIGNED_BYTE, GL_TRUE,
_color_stride, GL_PTR_OFFSET(_color_offset)));
// Binds mvp uniform
const GLint mvp_uniform = uniform(0);
const ozz::math::Float4x4 mvp = _view_proj * _model;
float values[16];
math::StorePtrU(mvp.cols[0], values + 0);
math::StorePtrU(mvp.cols[1], values + 4);
math::StorePtrU(mvp.cols[2], values + 8);
math::StorePtrU(mvp.cols[3], values + 12);
GL(UniformMatrix4fv(mvp_uniform, 1, false, values));
// Binds texture
const GLint texture = uniform(1);
GL(Uniform1i(texture, 0));
}
namespace {
const char* kPassUv =
"attribute vec2 a_uv;\n"
"varying vec2 v_vertex_uv;\n"
"void PassUv() {\n"
" v_vertex_uv = a_uv;\n"
"}\n";
const char* kPassNoUv =
"void PassUv() {\n"
"}\n";
const char* kShaderUberVS =
"uniform mat4 u_mvp;\n"
"attribute vec3 a_position;\n"
"attribute vec3 a_normal;\n"
"attribute vec4 a_color;\n"
"varying vec3 v_world_normal;\n"
"varying vec4 v_vertex_color;\n"
"void main() {\n"
" mat4 world_matrix = GetWorldMatrix();\n"
" vec4 vertex = vec4(a_position.xyz, 1.);\n"
" gl_Position = u_mvp * world_matrix * vertex;\n"
" mat3 cross_matrix = mat3(\n"
" cross(world_matrix[1].xyz, world_matrix[2].xyz),\n"
" cross(world_matrix[2].xyz, world_matrix[0].xyz),\n"
" cross(world_matrix[0].xyz, world_matrix[1].xyz));\n"
" float invdet = 1.0 / dot(cross_matrix[2], world_matrix[2].xyz);\n"
" mat3 normal_matrix = cross_matrix * invdet;\n"
" v_world_normal = normal_matrix * a_normal;\n"
" v_vertex_color = a_color;\n"
" PassUv();\n"
"}\n";
const char* kShaderAmbientFct =
"vec4 GetAmbient(vec3 _world_normal) {\n"
" vec3 normal = normalize(_world_normal);\n"
" vec3 alpha = (normal + 1.) * .5;\n"
" vec2 bt = mix(vec2(.3, .7), vec2(.4, .8), alpha.xz);\n"
" vec3 ambient = mix(vec3(bt.x, .3, bt.x), vec3(bt.y, .8, bt.y), "
"alpha.y);\n"
" return vec4(ambient, 1.);\n"
"}\n";
const char* kShaderAmbientFS =
"varying vec3 v_world_normal;\n"
"varying vec4 v_vertex_color;\n"
"void main() {\n"
" vec4 ambient = GetAmbient(v_world_normal);\n"
" gl_FragColor = ambient *\n"
" v_vertex_color;\n"
"}\n";
const char* kShaderAmbientTexturedFS =
"uniform sampler2D u_texture;\n"
"varying vec3 v_world_normal;\n"
"varying vec4 v_vertex_color;\n"
"varying vec2 v_vertex_uv;\n"
"void main() {\n"
" vec4 ambient = GetAmbient(v_world_normal);\n"
" gl_FragColor = ambient *\n"
" v_vertex_color *\n"
" texture2D(u_texture, v_vertex_uv);\n"
"}\n";
} // namespace
void SkeletonShader::Bind(const math::Float4x4& _model,
const math::Float4x4& _view_proj, GLsizei _pos_stride,
GLsizei _pos_offset, GLsizei _normal_stride,
GLsizei _normal_offset, GLsizei _color_stride,
GLsizei _color_offset) {
GL(UseProgram(program()));
const GLint position_attrib = attrib(0);
GL(EnableVertexAttribArray(position_attrib));
GL(VertexAttribPointer(position_attrib, 3, GL_FLOAT, GL_FALSE, _pos_stride,
GL_PTR_OFFSET(_pos_offset)));
const GLint normal_attrib = attrib(1);
GL(EnableVertexAttribArray(normal_attrib));
GL(VertexAttribPointer(normal_attrib, 3, GL_FLOAT, GL_FALSE, _normal_stride,
GL_PTR_OFFSET(_normal_offset)));
const GLint color_attrib = attrib(2);
GL(EnableVertexAttribArray(color_attrib));
GL(VertexAttribPointer(color_attrib, 4, GL_UNSIGNED_BYTE, GL_TRUE,
_color_stride, GL_PTR_OFFSET(_color_offset)));
// Binds mvp uniform
const GLint mvp_uniform = uniform(0);
const ozz::math::Float4x4 mvp = _view_proj * _model;
float values[16];
math::StorePtrU(mvp.cols[0], values + 0);
math::StorePtrU(mvp.cols[1], values + 4);
math::StorePtrU(mvp.cols[2], values + 8);
math::StorePtrU(mvp.cols[3], values + 12);
GL(UniformMatrix4fv(mvp_uniform, 1, false, values));
}
ozz::unique_ptr<JointShader> JointShader::Build() {
bool success = true;
const char* vs_joint_to_world_matrix =
"mat4 GetWorldMatrix() {\n"
" // Rebuilds joint matrix.\n"
" mat4 joint_matrix;\n"
" joint_matrix[0] = vec4(normalize(joint[0].xyz), 0.);\n"
" joint_matrix[1] = vec4(normalize(joint[1].xyz), 0.);\n"
" joint_matrix[2] = vec4(normalize(joint[2].xyz), 0.);\n"
" joint_matrix[3] = vec4(joint[3].xyz, 1.);\n"
" // Rebuilds bone properties.\n"
" vec3 bone_dir = vec3(joint[0].w, joint[1].w, joint[2].w);\n"
" float bone_len = length(bone_dir);\n"
" // Setup rendering world matrix.\n"
" mat4 world_matrix;\n"
" world_matrix[0] = joint_matrix[0] * bone_len;\n"
" world_matrix[1] = joint_matrix[1] * bone_len;\n"
" world_matrix[2] = joint_matrix[2] * bone_len;\n"
" world_matrix[3] = joint_matrix[3];\n"
" return world_matrix;\n"
"}\n";
const char* vs[] = {kPlatformSpecivicVSHeader, kPassNoUv,
GL_ARB_instanced_arrays_supported
? "attribute mat4 joint;\n"
: "uniform mat4 joint;\n",
vs_joint_to_world_matrix, kShaderUberVS};
const char* fs[] = {kPlatformSpecivicFSHeader, kShaderAmbientFct,
kShaderAmbientFS};
ozz::unique_ptr<JointShader> shader = make_unique<JointShader>();
success &=
shader->BuildFromSource(OZZ_ARRAY_SIZE(vs), vs, OZZ_ARRAY_SIZE(fs), fs);
// Binds default attributes
success &= shader->FindAttrib("a_position");
success &= shader->FindAttrib("a_normal");
success &= shader->FindAttrib("a_color");
// Binds default uniforms
success &= shader->BindUniform("u_mvp");
if (GL_ARB_instanced_arrays_supported) {
success &= shader->FindAttrib("joint");
} else {
success &= shader->BindUniform("joint");
}
if (!success) {
shader.reset();
}
return shader;
}
ozz::unique_ptr<BoneShader>
BoneShader::Build() { // Builds a world matrix from joint uniforms,
// sticking bone model between
bool success = true;
// parent and child joints.
const char* vs_joint_to_world_matrix =
"mat4 GetWorldMatrix() {\n"
" // Rebuilds bone properties.\n"
" // Bone length is set to zero to disable leaf rendering.\n"
" float is_bone = joint[3].w;\n"
" vec3 bone_dir = vec3(joint[0].w, joint[1].w, joint[2].w) * is_bone;\n"
" float bone_len = length(bone_dir);\n"
" // Setup rendering world matrix.\n"
" float dot1 = dot(joint[2].xyz, bone_dir);\n"
" float dot2 = dot(joint[0].xyz, bone_dir);\n"
" vec3 binormal = abs(dot1) < abs(dot2) ? joint[2].xyz : joint[0].xyz;\n"
" mat4 world_matrix;\n"
" world_matrix[0] = vec4(bone_dir, 0.);\n"
" world_matrix[1] = \n"
" vec4(bone_len * normalize(cross(binormal, bone_dir)), 0.);\n"
" world_matrix[2] =\n"
" vec4(bone_len * normalize(cross(bone_dir, world_matrix[1].xyz)), "
"0.);\n"
" world_matrix[3] = vec4(joint[3].xyz, 1.);\n"
" return world_matrix;\n"
"}\n";
const char* vs[] = {kPlatformSpecivicVSHeader, kPassNoUv,
GL_ARB_instanced_arrays_supported
? "attribute mat4 joint;\n"
: "uniform mat4 joint;\n",
vs_joint_to_world_matrix, kShaderUberVS};
const char* fs[] = {kPlatformSpecivicFSHeader, kShaderAmbientFct,
kShaderAmbientFS};
ozz::unique_ptr<BoneShader> shader = make_unique<BoneShader>();
success &=
shader->BuildFromSource(OZZ_ARRAY_SIZE(vs), vs, OZZ_ARRAY_SIZE(fs), fs);
// Binds default attributes
success &= shader->FindAttrib("a_position");
success &= shader->FindAttrib("a_normal");
success &= shader->FindAttrib("a_color");
// Binds default uniforms
success &= shader->BindUniform("u_mvp");
if (GL_ARB_instanced_arrays_supported) {
success &= shader->FindAttrib("joint");
} else {
success &= shader->BindUniform("joint");
}
if (!success) {
shader.reset();
}
return shader;
}
ozz::unique_ptr<AmbientShader> AmbientShader::Build() {
const char* vs[] = {
kPlatformSpecivicVSHeader, kPassNoUv,
"uniform mat4 u_mw;\n mat4 GetWorldMatrix() {return u_mw;}\n",
kShaderUberVS};
const char* fs[] = {kPlatformSpecivicFSHeader, kShaderAmbientFct,
kShaderAmbientFS};
ozz::unique_ptr<AmbientShader> shader = make_unique<AmbientShader>();
bool success =
shader->InternalBuild(OZZ_ARRAY_SIZE(vs), vs, OZZ_ARRAY_SIZE(fs), fs);
if (!success) {
shader.reset();
}
return shader;
}
bool AmbientShader::InternalBuild(int _vertex_count, const char** _vertex,
int _fragment_count, const char** _fragment) {
bool success = true;
success &=
BuildFromSource(_vertex_count, _vertex, _fragment_count, _fragment);
// Binds default attributes
success &= FindAttrib("a_position");
success &= FindAttrib("a_normal");
success &= FindAttrib("a_color");
// Binds default uniforms
success &= BindUniform("u_mw");
success &= BindUniform("u_mvp");
return success;
}
void AmbientShader::Bind(const math::Float4x4& _model,
const math::Float4x4& _view_proj, GLsizei _pos_stride,
GLsizei _pos_offset, GLsizei _normal_stride,
GLsizei _normal_offset, GLsizei _color_stride,
GLsizei _color_offset) {
GL(UseProgram(program()));
const GLint position_attrib = attrib(0);
GL(EnableVertexAttribArray(position_attrib));
GL(VertexAttribPointer(position_attrib, 3, GL_FLOAT, GL_FALSE, _pos_stride,
GL_PTR_OFFSET(_pos_offset)));
const GLint normal_attrib = attrib(1);
GL(EnableVertexAttribArray(normal_attrib));
GL(VertexAttribPointer(normal_attrib, 3, GL_FLOAT, GL_TRUE, _normal_stride,
GL_PTR_OFFSET(_normal_offset)));
const GLint color_attrib = attrib(2);
GL(EnableVertexAttribArray(color_attrib));
GL(VertexAttribPointer(color_attrib, 4, GL_UNSIGNED_BYTE, GL_TRUE,
_color_stride, GL_PTR_OFFSET(_color_offset)));
// Binds mw uniform
float values[16];
const GLint mw_uniform = uniform(0);
math::StorePtrU(_model.cols[0], values + 0);
math::StorePtrU(_model.cols[1], values + 4);
math::StorePtrU(_model.cols[2], values + 8);
math::StorePtrU(_model.cols[3], values + 12);
GL(UniformMatrix4fv(mw_uniform, 1, false, values));
// Binds mvp uniform
const GLint mvp_uniform = uniform(1);
math::StorePtrU(_view_proj.cols[0], values + 0);
math::StorePtrU(_view_proj.cols[1], values + 4);
math::StorePtrU(_view_proj.cols[2], values + 8);
math::StorePtrU(_view_proj.cols[3], values + 12);
GL(UniformMatrix4fv(mvp_uniform, 1, false, values));
}
ozz::unique_ptr<AmbientShaderInstanced> AmbientShaderInstanced::Build() {
bool success = true;
const char* vs[] = {
kPlatformSpecivicVSHeader, kPassNoUv,
"attribute mat4 a_mw;\n mat4 GetWorldMatrix() {return a_mw;}\n",
kShaderUberVS};
const char* fs[] = {kPlatformSpecivicFSHeader, kShaderAmbientFct,
kShaderAmbientFS};
ozz::unique_ptr<AmbientShaderInstanced> shader =
make_unique<AmbientShaderInstanced>();
success &=
shader->BuildFromSource(OZZ_ARRAY_SIZE(vs), vs, OZZ_ARRAY_SIZE(fs), fs);
// Binds default attributes
success &= shader->FindAttrib("a_position");
success &= shader->FindAttrib("a_normal");
success &= shader->FindAttrib("a_color");
success &= shader->FindAttrib("a_mw");
// Binds default uniforms
success &= shader->BindUniform("u_mvp");
if (!success) {
shader.reset();
}
return shader;
}
void AmbientShaderInstanced::Bind(GLsizei _models_offset,
const math::Float4x4& _view_proj,
GLsizei _pos_stride, GLsizei _pos_offset,
GLsizei _normal_stride,
GLsizei _normal_offset, GLsizei _color_stride,
GLsizei _color_offset) {
GL(UseProgram(program()));
const GLint position_attrib = attrib(0);
GL(EnableVertexAttribArray(position_attrib));
GL(VertexAttribPointer(position_attrib, 3, GL_FLOAT, GL_FALSE, _pos_stride,
GL_PTR_OFFSET(_pos_offset)));
const GLint normal_attrib = attrib(1);
GL(EnableVertexAttribArray(normal_attrib));
GL(VertexAttribPointer(normal_attrib, 3, GL_FLOAT, GL_TRUE, _normal_stride,
GL_PTR_OFFSET(_normal_offset)));
const GLint color_attrib = attrib(2);
GL(EnableVertexAttribArray(color_attrib));
GL(VertexAttribPointer(color_attrib, 4, GL_UNSIGNED_BYTE, GL_TRUE,
_color_stride, GL_PTR_OFFSET(_color_offset)));
if (_color_stride == 0) {
GL(VertexAttribDivisor_(color_attrib,
std::numeric_limits<unsigned int>::max()));
}
// Binds mw uniform
const GLint models_attrib = attrib(3);
GL(EnableVertexAttribArray(models_attrib + 0));
GL(EnableVertexAttribArray(models_attrib + 1));
GL(EnableVertexAttribArray(models_attrib + 2));
GL(EnableVertexAttribArray(models_attrib + 3));
GL(VertexAttribDivisor_(models_attrib + 0, 1));
GL(VertexAttribDivisor_(models_attrib + 1, 1));
GL(VertexAttribDivisor_(models_attrib + 2, 1));
GL(VertexAttribDivisor_(models_attrib + 3, 1));
GL(VertexAttribPointer(models_attrib + 0, 4, GL_FLOAT, GL_FALSE,
sizeof(math::Float4x4),
GL_PTR_OFFSET(0 + _models_offset)));
GL(VertexAttribPointer(models_attrib + 1, 4, GL_FLOAT, GL_FALSE,
sizeof(math::Float4x4),
GL_PTR_OFFSET(16 + _models_offset)));
GL(VertexAttribPointer(models_attrib + 2, 4, GL_FLOAT, GL_FALSE,
sizeof(math::Float4x4),
GL_PTR_OFFSET(32 + _models_offset)));
GL(VertexAttribPointer(models_attrib + 3, 4, GL_FLOAT, GL_FALSE,
sizeof(math::Float4x4),
GL_PTR_OFFSET(48 + _models_offset)));
// Binds mvp uniform
const GLint mvp_uniform = uniform(0);
float values[16];
math::StorePtrU(_view_proj.cols[0], values + 0);
math::StorePtrU(_view_proj.cols[1], values + 4);
math::StorePtrU(_view_proj.cols[2], values + 8);
math::StorePtrU(_view_proj.cols[3], values + 12);
GL(UniformMatrix4fv(mvp_uniform, 1, false, values));
}
void AmbientShaderInstanced::Unbind() {
const GLint color_attrib = attrib(2);
GL(VertexAttribDivisor_(color_attrib, 0));
const GLint models_attrib = attrib(3);
GL(DisableVertexAttribArray(models_attrib + 0));
GL(DisableVertexAttribArray(models_attrib + 1));
GL(DisableVertexAttribArray(models_attrib + 2));
GL(DisableVertexAttribArray(models_attrib + 3));
GL(VertexAttribDivisor_(models_attrib + 0, 0));
GL(VertexAttribDivisor_(models_attrib + 1, 0));
GL(VertexAttribDivisor_(models_attrib + 2, 0));
GL(VertexAttribDivisor_(models_attrib + 3, 0));
Shader::Unbind();
}
ozz::unique_ptr<AmbientTexturedShader> AmbientTexturedShader::Build() {
const char* vs[] = {
kPlatformSpecivicVSHeader, kPassUv,
"uniform mat4 u_mw;\n mat4 GetWorldMatrix() {return u_mw;}\n",
kShaderUberVS};
const char* fs[] = {kPlatformSpecivicFSHeader, kShaderAmbientFct,
kShaderAmbientTexturedFS};
ozz::unique_ptr<AmbientTexturedShader> shader =
make_unique<AmbientTexturedShader>();
bool success =
shader->InternalBuild(OZZ_ARRAY_SIZE(vs), vs, OZZ_ARRAY_SIZE(fs), fs);
success &= shader->FindAttrib("a_uv");
if (!success) {
shader.reset();
}
return shader;
}
void AmbientTexturedShader::Bind(const math::Float4x4& _model,
const math::Float4x4& _view_proj,
GLsizei _pos_stride, GLsizei _pos_offset,
GLsizei _normal_stride, GLsizei _normal_offset,
GLsizei _color_stride, GLsizei _color_offset,
GLsizei _uv_stride, GLsizei _uv_offset) {
AmbientShader::Bind(_model, _view_proj, _pos_stride, _pos_offset,
_normal_stride, _normal_offset, _color_stride,
_color_offset);
const GLint uv_attrib = attrib(3);
GL(EnableVertexAttribArray(uv_attrib));
GL(VertexAttribPointer(uv_attrib, 2, GL_FLOAT, GL_FALSE, _uv_stride,
GL_PTR_OFFSET(_uv_offset)));
}
} // namespace internal
} // namespace sample
} // namespace ozz
@@ -0,0 +1,249 @@
//----------------------------------------------------------------------------//
// //
// ozz-animation is hosted at http://github.com/guillaumeblanc/ozz-animation //
// and distributed under the MIT License (MIT). //
// //
// Copyright (c) Guillaume Blanc //
// //
// Permission is hereby granted, free of charge, to any person obtaining a //
// copy of this software and associated documentation files (the "Software"), //
// to deal in the Software without restriction, including without limitation //
// the rights to use, copy, modify, merge, publish, distribute, sublicense, //
// and/or sell copies of the Software, and to permit persons to whom the //
// Software is furnished to do so, subject to the following conditions: //
// //
// The above copyright notice and this permission notice shall be included in //
// all copies or substantial portions of the Software. //
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL //
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING //
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER //
// DEALINGS IN THE SOFTWARE. //
// //
//----------------------------------------------------------------------------//
#ifndef OZZ_SAMPLES_FRAMEWORK_INTERNAL_SHADER_H_
#define OZZ_SAMPLES_FRAMEWORK_INTERNAL_SHADER_H_
#include "ozz/base/memory/unique_ptr.h"
#include "renderer_impl.h"
namespace ozz {
namespace math {
struct Float4x4;
}
namespace sample {
namespace internal {
// Declares a shader program.
class Shader {
public:
// Construct a fixed function pipeline shader. Use Shader::Build to specify
// shader sources.
Shader();
// Destruct a shader.
virtual ~Shader();
// Returns the shader program that can be bound to the OpenGL context.
GLuint program() const { return program_; }
// Request an uniform location and pushes it to the uniform stack.
// The uniform location is then accessible thought uniform().
bool BindUniform(const char* _semantic);
// Get an uniform location from the stack at index _index.
GLint uniform(int _index) const { return uniforms_[_index]; }
// Request an attribute location and pushes it to the uniform stack.
// The varying location is then accessible thought attrib().
bool FindAttrib(const char* _semantic);
// Get an varying location from the stack at index _index.
GLint attrib(int _index) const { return attribs_[_index]; }
// Unblind shader.
virtual void Unbind();
protected:
// Constructs a shader from _vertex and _fragment glsl sources.
// Mutliple source files can be specified using the *count argument.
bool BuildFromSource(int _vertex_count, const char** _vertex,
int _fragment_count, const char** _fragment);
private:
// Unbind all attribs from GL.
void UnbindAttribs();
// Shader program
GLuint program_;
// Vertex and fragment shaders
GLuint vertex_;
GLuint fragment_;
// Uniform locations, in the order they were requested.
ozz::vector<GLint> uniforms_;
// Varying locations, in the order they were requested.
ozz::vector<GLint> attribs_;
};
class ImmediatePCShader : public Shader {
public:
ImmediatePCShader() {}
virtual ~ImmediatePCShader() {}
// Constructs the shader.
// Returns nullptr if shader compilation failed or a valid Shader pointer on
// success. The shader must then be deleted using default allocator Delete
// function.
static ozz::unique_ptr<ImmediatePCShader> Build();
// Binds the shader.
void Bind(const math::Float4x4& _model, const math::Float4x4& _view_proj,
GLsizei _pos_stride, GLsizei _pos_offset, GLsizei _color_stride,
GLsizei _color_offset);
};
class ImmediatePTCShader : public Shader {
public:
ImmediatePTCShader() {}
virtual ~ImmediatePTCShader() {}
// Constructs the shader.
// Returns nullptr if shader compilation failed or a valid Shader pointer on
// success. The shader must then be deleted using default allocator Delete
// function.
static ozz::unique_ptr<ImmediatePTCShader> Build();
// Binds the shader.
void Bind(const math::Float4x4& _model, const math::Float4x4& _view_proj,
GLsizei _pos_stride, GLsizei _pos_offset, GLsizei _tex_stride,
GLsizei _tex_offset, GLsizei _color_stride, GLsizei _color_offset);
};
class SkeletonShader : public Shader {
public:
SkeletonShader() {}
virtual ~SkeletonShader() {}
// Binds the shader.
void Bind(const math::Float4x4& _model, const math::Float4x4& _view_proj,
GLsizei _pos_stride, GLsizei _pos_offset, GLsizei _normal_stride,
GLsizei _normal_offset, GLsizei _color_stride,
GLsizei _color_offset);
// Get an attribute location for the join, in cased of instanced rendering.
GLint joint_instanced_attrib() const { return attrib(3); }
// Get an uniform location for the join, in cased of non-instanced rendering.
GLint joint_uniform() const { return uniform(1); }
};
class JointShader : public SkeletonShader {
public:
JointShader() {}
virtual ~JointShader() {}
// Constructs the shader.
// Returns nullptr if shader compilation failed or a valid Shader pointer on
// success. The shader must then be deleted using default allocator Delete
// function.
static ozz::unique_ptr<JointShader> Build();
};
class BoneShader : public SkeletonShader {
public:
BoneShader() {}
virtual ~BoneShader() {}
// Constructs the shader.
// Returns nullptr if shader compilation failed or a valid Shader pointer on
// success. The shader must then be deleted using default allocator Delete
// function.
static ozz::unique_ptr<BoneShader> Build();
};
class AmbientShader : public Shader {
public:
AmbientShader() {}
virtual ~AmbientShader() {}
// Constructs the shader.
// Returns nullptr if shader compilation failed or a valid Shader pointer on
// success. The shader must then be deleted using default allocator Delete
// function.
static ozz::unique_ptr<AmbientShader> Build();
// Binds the shader.
void Bind(const math::Float4x4& _model, const math::Float4x4& _view_proj,
GLsizei _pos_stride, GLsizei _pos_offset, GLsizei _normal_stride,
GLsizei _normal_offset, GLsizei _color_stride,
GLsizei _color_offset);
protected:
bool InternalBuild(int _vertex_count, const char** _vertex,
int _fragment_count, const char** _fragment);
};
class AmbientShaderInstanced : public Shader {
public:
AmbientShaderInstanced() {}
virtual ~AmbientShaderInstanced() {}
// Constructs the shader.
// Returns nullptr if shader compilation failed or a valid Shader pointer on
// success. The shader must then be deleted using default allocator Delete
// function.
static ozz::unique_ptr<AmbientShaderInstanced> Build();
// Binds the shader.
void Bind(GLsizei _models_offset, const math::Float4x4& _view_proj,
GLsizei _pos_stride, GLsizei _pos_offset, GLsizei _normal_stride,
GLsizei _normal_offset, GLsizei _color_stride,
GLsizei _color_offset);
virtual void Unbind();
};
class AmbientTexturedShader : public AmbientShader {
public:
// Constructs the shader.
// Returns nullptr if shader compilation failed or a valid Shader pointer on
// success. The shader must then be deleted using default allocator Delete
// function.
static ozz::unique_ptr<AmbientTexturedShader> Build();
// Binds the shader.
void Bind(const math::Float4x4& _model, const math::Float4x4& _view_proj,
GLsizei _pos_stride, GLsizei _pos_offset, GLsizei _normal_stride,
GLsizei _normal_offset, GLsizei _color_stride,
GLsizei _color_offset, GLsizei _uv_stride, GLsizei _uv_offset);
};
/*
class AmbientTexturedShaderInstanced : public AmbientShaderInstanced {
public:
// Constructs the shader.
// Returns nullptr if shader compilation failed or a valid Shader pointer on
// success. The shader must then be deleted using default allocator Delete
// function.
static AmbientTexturedShaderInstanced* Build();
// Binds the shader.
void Bind(GLsizei _models_offset,
const math::Float4x4& _view_proj,
GLsizei _pos_stride, GLsizei _pos_offset,
GLsizei _normal_stride, GLsizei _normal_offset,
GLsizei _color_stride, GLsizei _color_offset,
GLsizei _uv_stride, GLsizei _uv_offset);
};
*/
} // namespace internal
} // namespace sample
} // namespace ozz
#endif // OZZ_SAMPLES_FRAMEWORK_INTERNAL_SHADER_H_
@@ -0,0 +1,220 @@
//----------------------------------------------------------------------------//
// //
// ozz-animation is hosted at http://github.com/guillaumeblanc/ozz-animation //
// and distributed under the MIT License (MIT). //
// //
// Copyright (c) Guillaume Blanc //
// //
// Permission is hereby granted, free of charge, to any person obtaining a //
// copy of this software and associated documentation files (the "Software"), //
// to deal in the Software without restriction, including without limitation //
// the rights to use, copy, modify, merge, publish, distribute, sublicense, //
// and/or sell copies of the Software, and to permit persons to whom the //
// Software is furnished to do so, subject to the following conditions: //
// //
// The above copyright notice and this permission notice shall be included in //
// all copies or substantial portions of the Software. //
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL //
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING //
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER //
// DEALINGS IN THE SOFTWARE. //
// //
//----------------------------------------------------------------------------//
#define OZZ_INCLUDE_PRIVATE_HEADER // Allows to include private headers.
#include "shooter.h"
#include <cassert>
#include <cstdio>
#include "renderer_impl.h"
namespace ozz {
namespace sample {
namespace internal {
Shooter::Shooter()
: gl_shot_format_(GL_RGBA), // Default fail safe format and types.
image_format_(image::Format::kRGBA),
shot_number_(0) {
// Test required extension (optional for the framework).
// If GL_VERSION_1_5 is defined (aka OZZ_GL_VERSION_1_5_EXT not defined), then
// these functions are part of the library (don't need extensions).
supported_ = true;
#ifdef OZZ_GL_VERSION_1_5_EXT
supported_ &= glMapBuffer != nullptr && glUnmapBuffer != nullptr;
#endif // OZZ_GL_VERSION_1_5_EXT
// Initializes shots
GLuint pbos[kNumShots];
GL(GenBuffers(kNumShots, pbos));
for (int i = 0; i < kNumShots; ++i) {
Shot& shot = shots_[i];
shot.pbo = pbos[i];
}
// OpenGL ES2 compatibility extension allows to query for implementation best
// format and type.
if (glfwExtensionSupported("GL_ARB_ES2_compatibility") != 0) {
GLint format;
GL(GetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES, &format));
GLint type;
GL(GetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE_OES, &type));
// Only support GL_UNSIGNED_BYTE.
if (type == GL_UNSIGNED_BYTE) {
switch (format) {
case GL_RGBA:
gl_shot_format_ = format;
image_format_ = image::Format::kRGBA;
break;
case GL_BGRA:
gl_shot_format_ = format;
image_format_ = image::Format::kBGRA;
break;
case GL_RGB:
gl_shot_format_ = format;
image_format_ = image::Format::kRGB;
break;
case GL_BGR:
gl_shot_format_ = format;
image_format_ = image::Format::kBGR;
break;
}
}
} else {
// Default fail safe format and types.
gl_shot_format_ = GL_RGBA;
image_format_ = image::Format::kRGBA;
}
}
Shooter::~Shooter() {
// Process all remaining shots.
ProcessAll();
// Clean shot pbos.
for (int i = 0; i < kNumShots; ++i) {
Shot& shot = shots_[i];
GL(DeleteBuffers(1, &shot.pbo));
assert(shot.cooldown == 0); // Must have been processed.
}
}
void Shooter::Resize(int _width, int _height) {
// Early out if not supported.
if (!supported_) {
return;
}
// Process all remaining shots.
ProcessAll();
// Resizes all pbos.
#ifndef EMSCRIPTEN
for (int i = 0; i < kNumShots; ++i) {
Shot& shot = shots_[i];
GL(BindBuffer(GL_PIXEL_PACK_BUFFER, shot.pbo));
GL(BufferData(GL_PIXEL_PACK_BUFFER, _width * _height * 4, 0,
GL_STREAM_READ));
shot.width = _width;
shot.height = _height;
assert(shot.cooldown == 0); // Must have been processed.
}
GL(BindBuffer(GL_PIXEL_PACK_BUFFER, 0));
#endif // EMSCRIPTEN
}
bool Shooter::Update() { return Process(); }
bool Shooter::Process() {
// Early out if not supported.
if (!supported_) {
return true;
}
// Early out if process stack is empty.
for (int i = 0; i < kNumShots; ++i) {
Shot& shot = shots_[i];
// Early out for already processed, or empty shots.
if (shot.cooldown == 0) {
continue;
}
// Skip shots that didn't reached the cooldown.
if (--shot.cooldown != 0) {
continue;
}
// Processes this shot.
#ifdef EMSCRIPTEN
(void)shot_number_;
#else // EMSCRIPTEN
GL(BindBuffer(GL_PIXEL_PACK_BUFFER, shot.pbo));
const void* pixels = glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY);
if (pixels) {
char name[16];
sprintf(name, "%06d.tga", shot_number_++);
ozz::sample::image::WriteTGA(name, shot.width, shot.height, image_format_,
reinterpret_cast<const uint8_t*>(pixels),
false);
GL(UnmapBuffer(GL_PIXEL_PACK_BUFFER));
}
GL(BindBuffer(GL_PIXEL_PACK_BUFFER, 0));
#endif // EMSCRIPTEN
}
return true;
}
bool Shooter::ProcessAll() {
// Reset cooldown to 1 for all "unprocessed" shots, so they will be
// "processed".
for (int i = 0; i < kNumShots; ++i) {
Shot& shot = shots_[i];
shot.cooldown = shot.cooldown > 0 ? 1 : 0;
}
return Process();
}
bool Shooter::Capture(int _buffer) {
assert(_buffer == GL_FRONT || _buffer == GL_BACK);
// Early out if not supported.
if (!supported_) {
return true;
}
// Finds the shot to use for this capture.
Shot* shot;
for (shot = shots_; shot < shots_ + kNumShots && shot->cooldown != 0;
++shot) {
}
assert(shot != shots_ + kNumShots);
// Initializes cooldown.
shot->cooldown = kInitialCountDown;
#ifndef EMSCRIPTEN
// Copy pixels to shot's pbo.
GL(ReadBuffer(_buffer));
GL(BindBuffer(GL_PIXEL_PACK_BUFFER, shot->pbo));
GL(PixelStorei(GL_PACK_ALIGNMENT, 4));
GL(ReadPixels(0, 0, shot->width, shot->height, gl_shot_format_,
GL_UNSIGNED_BYTE, 0));
GL(BindBuffer(GL_PIXEL_PACK_BUFFER, 0));
#endif // EMSCRIPTEN
return true;
}
} // namespace internal
} // namespace sample
} // namespace ozz
@@ -0,0 +1,95 @@
//----------------------------------------------------------------------------//
// //
// ozz-animation is hosted at http://github.com/guillaumeblanc/ozz-animation //
// and distributed under the MIT License (MIT). //
// //
// Copyright (c) Guillaume Blanc //
// //
// Permission is hereby granted, free of charge, to any person obtaining a //
// copy of this software and associated documentation files (the "Software"), //
// to deal in the Software without restriction, including without limitation //
// the rights to use, copy, modify, merge, publish, distribute, sublicense, //
// and/or sell copies of the Software, and to permit persons to whom the //
// Software is furnished to do so, subject to the following conditions: //
// //
// The above copyright notice and this permission notice shall be included in //
// all copies or substantial portions of the Software. //
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL //
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING //
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER //
// DEALINGS IN THE SOFTWARE. //
// //
//----------------------------------------------------------------------------//
#ifndef OZZ_SAMPLES_FRAMEWORK_INTERNAL_SHOOTER_H_
#define OZZ_SAMPLES_FRAMEWORK_INTERNAL_SHOOTER_H_
#include "ozz/base/containers/vector.h"
#include "framework/image.h"
namespace ozz {
namespace sample {
namespace internal {
// Implements GL screen shot and video shooter
class Shooter {
public:
Shooter();
virtual ~Shooter();
// Resize notification, used to resize memory buffers.
void Resize(int _width, int _height);
// Updates shooter (output captured buffers to memory).
bool Update();
// Captures current (GL_FRONT or GL_BACK) _buffer to memory.
bool Capture(int _buffer);
private:
// Updates all cooldowns and process terminated shots. Returns false if it
// fails, true on success or empty stack.
bool Process();
// Processes all shots from the stack. Returns false if it fails, true on
// success or empty stack.
bool ProcessAll();
// Defines shot buffer (pbo) and data.
struct Shot {
unsigned int pbo;
int width;
int height;
int cooldown; // Shot is processed when cooldown falls to 0.
Shot() : pbo(0), width(0), height(0), cooldown(0) {}
};
// Array of pre-allocated shots, used to allow asynchronous dma transfers of
// pbos.
enum {
kInitialCountDown = 2, // Allows to delay pbo mapping 2 rendering frames.
kNumShots = kInitialCountDown,
};
Shot shots_[kNumShots];
// Format of pixels to use to glReadPixels calls.
int gl_shot_format_;
// Image format that matches GL format.
image::Format::Value image_format_;
// Shot number, used to name images.
int shot_number_;
// Is the shooter functionality supported.
bool supported_;
};
} // namespace internal
} // namespace sample
} // namespace ozz
#endif // OZZ_SAMPLES_FRAMEWORK_INTERNAL_SHOOTER_H_
+95
View File
@@ -0,0 +1,95 @@
//----------------------------------------------------------------------------//
// //
// ozz-animation is hosted at http://github.com/guillaumeblanc/ozz-animation //
// and distributed under the MIT License (MIT). //
// //
// Copyright (c) Guillaume Blanc //
// //
// Permission is hereby granted, free of charge, to any person obtaining a //
// copy of this software and associated documentation files (the "Software"), //
// to deal in the Software without restriction, including without limitation //
// the rights to use, copy, modify, merge, publish, distribute, sublicense, //
// and/or sell copies of the Software, and to permit persons to whom the //
// Software is furnished to do so, subject to the following conditions: //
// //
// The above copyright notice and this permission notice shall be included in //
// all copies or substantial portions of the Software. //
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL //
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING //
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER //
// DEALINGS IN THE SOFTWARE. //
// //
//----------------------------------------------------------------------------//
#include "mesh.h"
#include "ozz/base/containers/vector_archive.h"
#include "ozz/base/memory/allocator.h"
#include "ozz/base/io/archive.h"
#include "ozz/base/maths/math_archive.h"
#include "ozz/base/maths/simd_math_archive.h"
namespace ozz {
namespace io {
void Extern<sample::Mesh::Part>::Save(OArchive& _archive,
const sample::Mesh::Part* _parts,
size_t _count) {
for (size_t i = 0; i < _count; ++i) {
const sample::Mesh::Part& part = _parts[i];
_archive << part.positions;
_archive << part.normals;
_archive << part.tangents;
_archive << part.uvs;
_archive << part.colors;
_archive << part.joint_indices;
_archive << part.joint_weights;
}
}
void Extern<sample::Mesh::Part>::Load(IArchive& _archive,
sample::Mesh::Part* _parts, size_t _count,
uint32_t _version) {
(void)_version;
for (size_t i = 0; i < _count; ++i) {
sample::Mesh::Part& part = _parts[i];
_archive >> part.positions;
_archive >> part.normals;
_archive >> part.tangents;
_archive >> part.uvs;
_archive >> part.colors;
_archive >> part.joint_indices;
_archive >> part.joint_weights;
}
}
void Extern<sample::Mesh>::Save(OArchive& _archive, const sample::Mesh* _meshes,
size_t _count) {
for (size_t i = 0; i < _count; ++i) {
const sample::Mesh& mesh = _meshes[i];
_archive << mesh.parts;
_archive << mesh.triangle_indices;
_archive << mesh.joint_remaps;
_archive << mesh.inverse_bind_poses;
}
}
void Extern<sample::Mesh>::Load(IArchive& _archive, sample::Mesh* _meshes,
size_t _count, uint32_t _version) {
(void)_version;
for (size_t i = 0; i < _count; ++i) {
sample::Mesh& mesh = _meshes[i];
_archive >> mesh.parts;
_archive >> mesh.triangle_indices;
_archive >> mesh.joint_remaps;
_archive >> mesh.inverse_bind_poses;
}
}
} // namespace io
} // namespace ozz
+172
View File
@@ -0,0 +1,172 @@
//----------------------------------------------------------------------------//
// //
// ozz-animation is hosted at http://github.com/guillaumeblanc/ozz-animation //
// and distributed under the MIT License (MIT). //
// //
// Copyright (c) Guillaume Blanc //
// //
// Permission is hereby granted, free of charge, to any person obtaining a //
// copy of this software and associated documentation files (the "Software"), //
// to deal in the Software without restriction, including without limitation //
// the rights to use, copy, modify, merge, publish, distribute, sublicense, //
// and/or sell copies of the Software, and to permit persons to whom the //
// Software is furnished to do so, subject to the following conditions: //
// //
// The above copyright notice and this permission notice shall be included in //
// all copies or substantial portions of the Software. //
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL //
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING //
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER //
// DEALINGS IN THE SOFTWARE. //
// //
//----------------------------------------------------------------------------//
#ifndef OZZ_SAMPLES_FRAMEWORK_MESH_H_
#define OZZ_SAMPLES_FRAMEWORK_MESH_H_
#include "ozz/base/containers/vector.h"
#include "ozz/base/io/archive_traits.h"
#include "ozz/base/maths/simd_math.h"
#include "ozz/base/maths/vec_float.h"
#include "ozz/base/platform.h"
namespace ozz {
namespace sample {
// Defines a mesh with skinning information (joint indices and weights).
// The mesh is subdivided into parts that group vertices according to their
// number of influencing joints. Triangle indices are shared across mesh parts.
struct Mesh {
// Number of triangle indices for the mesh.
int triangle_index_count() const {
return static_cast<int>(triangle_indices.size());
}
// Number of vertices for all mesh parts.
int vertex_count() const {
int vertex_count = 0;
for (size_t i = 0; i < parts.size(); ++i) {
vertex_count += parts[i].vertex_count();
}
return vertex_count;
}
// Maximum number of joints influences for all mesh parts.
int max_influences_count() const {
int max_influences_count = 0;
for (size_t i = 0; i < parts.size(); ++i) {
const int influences_count = parts[i].influences_count();
max_influences_count = influences_count > max_influences_count
? influences_count
: max_influences_count;
}
return max_influences_count;
}
// Test if the mesh has skinning informations.
bool skinned() const {
for (size_t i = 0; i < parts.size(); ++i) {
if (parts[i].influences_count() != 0) {
return true;
}
}
return false;
}
// Returns the number of joints used to skin the mesh.
int num_joints() const { return static_cast<int>(inverse_bind_poses.size()); }
// Returns the highest joint number used in the skeleton.
int highest_joint_index() const {
// Takes advantage that joint_remaps is sorted.
return joint_remaps.size() != 0 ? static_cast<int>(joint_remaps.back()) : 0;
}
// Defines a portion of the mesh. A mesh is subdivided in sets of vertices
// with the same number of joint influences.
struct Part {
int vertex_count() const { return static_cast<int>(positions.size()) / 3; }
int influences_count() const {
const int _vertex_count = vertex_count();
if (_vertex_count == 0) {
return 0;
}
return static_cast<int>(joint_indices.size()) / _vertex_count;
}
typedef ozz::vector<float> Positions;
Positions positions;
enum { kPositionsCpnts = 3 }; // x, y, z components
typedef ozz::vector<float> Normals;
Normals normals;
enum { kNormalsCpnts = 3 }; // x, y, z components
typedef ozz::vector<float> Tangents;
Tangents tangents;
enum { kTangentsCpnts = 4 }; // x, y, z, right or left handed.
typedef ozz::vector<float> UVs;
UVs uvs; // u, v components
enum { kUVsCpnts = 2 };
typedef ozz::vector<uint8_t> Colors;
Colors colors;
enum { kColorsCpnts = 4 }; // r, g, b, a components
typedef ozz::vector<uint16_t> JointIndices;
JointIndices joint_indices; // Stride equals influences_count
typedef ozz::vector<float> JointWeights;
JointWeights joint_weights; // Stride equals influences_count - 1
};
typedef ozz::vector<Part> Parts;
Parts parts;
// Triangles indices. Indices are shared across all parts.
typedef ozz::vector<uint16_t> TriangleIndices;
TriangleIndices triangle_indices;
// Joints remapping indices. As a skin might be influenced by a part of the
// skeleton only, joint indices and inverse bind pose matrices are reordered
// to contain only used ones. Note that this array is sorted.
typedef ozz::vector<uint16_t> JointRemaps;
JointRemaps joint_remaps;
// Inverse bind-pose matrices. These are only available for skinned meshes.
typedef ozz::vector<ozz::math::Float4x4> InversBindPoses;
InversBindPoses inverse_bind_poses;
};
} // namespace sample
namespace io {
OZZ_IO_TYPE_TAG("ozz-sample-Mesh-Part", sample::Mesh::Part)
OZZ_IO_TYPE_VERSION(1, sample::Mesh::Part)
template <>
struct Extern<sample::Mesh::Part> {
static void Save(OArchive& _archive, const sample::Mesh::Part* _parts,
size_t _count);
static void Load(IArchive& _archive, sample::Mesh::Part* _parts,
size_t _count, uint32_t _version);
};
OZZ_IO_TYPE_TAG("ozz-sample-Mesh", sample::Mesh)
OZZ_IO_TYPE_VERSION(1, sample::Mesh)
template <>
struct Extern<sample::Mesh> {
static void Save(OArchive& _archive, const sample::Mesh* _meshes,
size_t _count);
static void Load(IArchive& _archive, sample::Mesh* _meshes, size_t _count,
uint32_t _version);
};
} // namespace io
} // namespace ozz
#endif // OZZ_SAMPLES_FRAMEWORK_MESH_H_
+117
View File
@@ -0,0 +1,117 @@
//----------------------------------------------------------------------------//
// //
// ozz-animation is hosted at http://github.com/guillaumeblanc/ozz-animation //
// and distributed under the MIT License (MIT). //
// //
// Copyright (c) Guillaume Blanc //
// //
// Permission is hereby granted, free of charge, to any person obtaining a //
// copy of this software and associated documentation files (the "Software"), //
// to deal in the Software without restriction, including without limitation //
// the rights to use, copy, modify, merge, publish, distribute, sublicense, //
// and/or sell copies of the Software, and to permit persons to whom the //
// Software is furnished to do so, subject to the following conditions: //
// //
// The above copyright notice and this permission notice shall be included in //
// all copies or substantial portions of the Software. //
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL //
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING //
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER //
// DEALINGS IN THE SOFTWARE. //
// //
//----------------------------------------------------------------------------//
#define OZZ_INCLUDE_PRIVATE_HEADER // Allows to include private headers.
#include "framework/profile.h"
#include "framework/internal/renderer_impl.h"
#include <cfloat>
#include <cmath>
#include "ozz/base/memory/allocator.h"
namespace ozz {
namespace sample {
Profiler::Profiler(Record* _record)
: begin_(static_cast<float>(glfwGetTime())), record_(_record) {}
Profiler::~Profiler() {
if (record_) {
float end = static_cast<float>(glfwGetTime());
record_->Push((end - begin_) * 1000.f);
}
}
Record::Record(int _max_records)
: max_records_(_max_records < 1 ? 1 : _max_records),
records_end_(
reinterpret_cast<float*>(memory::default_allocator()->Allocate(
_max_records * sizeof(float), alignof(float))) +
max_records_),
records_begin_(records_end_),
cursor_(records_end_) {}
Record::~Record() {
memory::default_allocator()->Deallocate(records_end_ - max_records_);
}
void Record::Push(float _value) {
if (records_begin_ + max_records_ == records_end_) {
if (cursor_ == records_begin_) { // Looping...
cursor_ = records_begin_ + max_records_;
}
} else {
// The buffer is not full yet.
records_begin_--;
}
--cursor_;
*cursor_ = _value;
}
Record::Statistics Record::GetStatistics() {
Statistics statistics = {FLT_MAX, -FLT_MAX, 0.f, 0.f};
if (records_begin_ == records_end_) { // No record.
return statistics;
}
// Computes statistics.
float sum = 0.f;
const float* current = cursor_;
const float* end = records_end_;
while (current < end) {
if (*current < statistics.min) {
statistics.min = *current;
}
if (*current > statistics.max) {
statistics.max = *current;
}
sum += *current;
++current;
if (current == records_end_) { // Looping...
end = cursor_;
current = records_begin_;
}
}
// Stores outputs.
/*
int exponent;
std::frexp(_f, &exponent);
float upper = pow(2.f, exponent);
statistics.max = func(statistics.max);*/
statistics.latest = *cursor_;
statistics.mean = sum / (records_end_ - records_begin_);
return statistics;
}
} // namespace sample
} // namespace ozz
+117
View File
@@ -0,0 +1,117 @@
//----------------------------------------------------------------------------//
// //
// ozz-animation is hosted at http://github.com/guillaumeblanc/ozz-animation //
// and distributed under the MIT License (MIT). //
// //
// Copyright (c) Guillaume Blanc //
// //
// Permission is hereby granted, free of charge, to any person obtaining a //
// copy of this software and associated documentation files (the "Software"), //
// to deal in the Software without restriction, including without limitation //
// the rights to use, copy, modify, merge, publish, distribute, sublicense, //
// and/or sell copies of the Software, and to permit persons to whom the //
// Software is furnished to do so, subject to the following conditions: //
// //
// The above copyright notice and this permission notice shall be included in //
// all copies or substantial portions of the Software. //
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL //
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING //
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER //
// DEALINGS IN THE SOFTWARE. //
// //
//----------------------------------------------------------------------------//
#ifndef OZZ_SAMPLES_FRAMEWORK_PROFILE_H_
#define OZZ_SAMPLES_FRAMEWORK_PROFILE_H_
namespace ozz {
namespace sample {
// Records up to a maximum number of float values. Once the maximum number is
// reached, it keeps the most recent ones and reject the oldest.
class Record {
public:
// Constructs and sets the maximum number of record-able values.
// The minimum record-able number of values is 1.
explicit Record(int _max_records);
// Deallocate records.
~Record();
// Adds _value to the records, while rejecting the oldest one if the maximum
// number is reached.
void Push(float _value);
// Returns the cursor to the newest value in the circular buffer.
// cursor() == record_begin() == record_end() if record is empty.
// Recorded values can be accessed sequentially from the newest to the oldest
// from [cursor():record_end()[ and then [record_begin():cursor()[
const float* cursor() const { return cursor_; }
// Returns the beginning of the recorded values.
const float* record_begin() const { return records_begin_; }
// Returns the end of the recorded values.
const float* record_end() const { return records_end_; }
// Statistics returned by GetStatistics function.
struct Statistics {
// Minimum value of the recorded range.
float min;
// Maximum value of the recorded range.
float max;
// Mean value of the recorded range.
float mean;
// Latest value of the recorded range.
float latest;
};
// Builds statistics of the current record state.
Statistics GetStatistics();
private:
// Disables assignment and copy.
Record(const Record& _record);
void operator=(const Record& _record);
// The maximum number of recorded entries.
int max_records_;
// Circular buffer of recorded valued, limited to max_records_ entries.
// records_begin_ is set to records_end_ when record is empty.
// records_begin_ then moves down to allocation begin. Therefore
// deallocation should be done using records_end_.
float* const records_end_;
float* records_begin_;
// Cursor in the circular buffer. Points to the latest pushed value.
float* cursor_;
};
// Measures the time spent between the constructor and the destructor (as a
// RAII object) and pushes the result to a Record.
class Profiler {
public:
// Starts measurement.
explicit Profiler(Record* _record);
// Ends measurement and pushes the result to the record.
~Profiler();
private:
// Disables assignment and copy.
Profiler(const Profiler& _profiler);
void operator=(const Profiler& _profiler);
// The time at which profiling began.
float begin_;
// Profiling result is pushed in the record_ object.
Record* record_;
};
} // namespace sample
} // namespace ozz
#endif // OZZ_SAMPLES_FRAMEWORK_PROFILE_H_
+182
View File
@@ -0,0 +1,182 @@
//----------------------------------------------------------------------------//
// //
// ozz-animation is hosted at http://github.com/guillaumeblanc/ozz-animation //
// and distributed under the MIT License (MIT). //
// //
// Copyright (c) Guillaume Blanc //
// //
// Permission is hereby granted, free of charge, to any person obtaining a //
// copy of this software and associated documentation files (the "Software"), //
// to deal in the Software without restriction, including without limitation //
// the rights to use, copy, modify, merge, publish, distribute, sublicense, //
// and/or sell copies of the Software, and to permit persons to whom the //
// Software is furnished to do so, subject to the following conditions: //
// //
// The above copyright notice and this permission notice shall be included in //
// all copies or substantial portions of the Software. //
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL //
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING //
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER //
// DEALINGS IN THE SOFTWARE. //
// //
//----------------------------------------------------------------------------//
#ifndef OZZ_SAMPLES_FRAMEWORK_RENDERER_H_
#define OZZ_SAMPLES_FRAMEWORK_RENDERER_H_
#include "ozz/base/platform.h"
#include "ozz/base/span.h"
namespace ozz {
namespace animation {
class Skeleton;
}
namespace math {
struct Float4x4;
struct Float3;
struct Box;
} // namespace math
namespace sample {
// Sample framework mesh type.
struct Mesh;
// Defines render Color structure.
struct Color {
unsigned char r, g, b, a;
};
// Color constants.
static const Color kRed = {0xff, 0, 0, 0xff};
static const Color kGreen = {0, 0xff, 0, 0xff};
static const Color kBlue = {0, 0, 0xff, 0xff};
static const Color kWhite = {0xff, 0xff, 0xff, 0xff};
static const Color kYellow = {0xff, 0xff, 0, 0xff};
static const Color kMagenta = {0xff, 0, 0xff, 0xff};
static const Color kCyan = {0, 0xff, 0xff, 0xff};
static const Color kGrey = {0x80, 0x80, 0x80, 0xff};
static const Color kBlack = {0x80, 0x80, 0x80, 0xff};
// Defines renderer abstract interface.
class Renderer {
public:
// Declares a virtual destructor to allow proper destruction.
virtual ~Renderer() {}
// Initializes he renderer.
// Return true on success.
virtual bool Initialize() = 0;
// Renders coordinate system axes: X in red, Y in green and W in blue.
// Axes size is given by _scale argument.
virtual bool DrawAxes(const ozz::math::Float4x4& _transform) = 0;
// Renders a square grid of _cell_count cells width, where each square cell
// has a size of _cell_size.
virtual bool DrawGrid(int _cell_count, float _cell_size) = 0;
// Renders a skeleton in its bind pose posture.
virtual bool DrawSkeleton(const animation::Skeleton& _skeleton,
const ozz::math::Float4x4& _transform,
bool _draw_joints = true) = 0;
// Renders a skeleton at the specified _position in the posture given by model
// space _matrices.
// Returns true on success, or false if _matrices range does not match with
// the _skeleton.
virtual bool DrawPosture(const animation::Skeleton& _skeleton,
ozz::span<const ozz::math::Float4x4> _matrices,
const ozz::math::Float4x4& _transform,
bool _draw_joints = true) = 0;
// Renders a box at a specified location.
// The 2 slots of _colors array respectively defines color of the filled
// faces and color of the box outlines.
virtual bool DrawBoxIm(const ozz::math::Box& _box,
const ozz::math::Float4x4& _transform,
const Color _colors[2]) = 0;
// Renders shaded boxes at specified locations.
virtual bool DrawBoxShaded(const ozz::math::Box& _box,
ozz::span<const ozz::math::Float4x4> _transforms,
Color _color) = 0;
// Renders a sphere at a specified location.
virtual bool DrawSphereIm(float _radius,
const ozz::math::Float4x4& _transform,
const Color _color) = 0;
// Renders shaded spheres at specified locations.
virtual bool DrawSphereShaded(
float _radius, ozz::span<const ozz::math::Float4x4> _transforms,
Color _color) = 0;
struct Options {
bool texture; // Show texture (default checkered texture).
bool normals; // Show normals.
bool tangents; // Show tangents.
bool binormals; // Show binormals, computed from the normal and tangent.
bool colors; // Show vertex colors.
bool wireframe; // Show vertex colors.
bool skip_skinning; // Show texture (default checkered texture).
Options()
: texture(false),
normals(false),
tangents(false),
binormals(false),
colors(false),
wireframe(false),
skip_skinning(false) {}
Options(bool _texture, bool _normals, bool _tangents, bool _binormals,
bool _colors, bool _wireframe, bool _skip_skinning)
: texture(_texture),
normals(_normals),
tangents(_tangents),
binormals(_binormals),
colors(_colors),
wireframe(_wireframe),
skip_skinning(_skip_skinning) {}
};
// Renders a skinned mesh at a specified location.
virtual bool DrawSkinnedMesh(const Mesh& _mesh,
const span<math::Float4x4> _skinning_matrices,
const ozz::math::Float4x4& _transform,
const Options& _options = Options()) = 0;
// Renders a mesh at a specified location.
virtual bool DrawMesh(const Mesh& _mesh,
const ozz::math::Float4x4& _transform,
const Options& _options = Options()) = 0;
// Renders a segment from begin to end.
virtual bool DrawSegment(const math::Float3& _begin, const math::Float3& _end,
Color _color,
const ozz::math::Float4x4& _transform) = 0;
// Renders vectors, defined by their starting point and a direction.
virtual bool DrawVectors(ozz::span<const float> _positions,
size_t _positions_stride,
ozz::span<const float> _directions,
size_t _directions_stride, int _num_vectors,
float _vector_length, Color _color,
const ozz::math::Float4x4& _transform) = 0;
// Compute binormals from normals and tangents, before displaying them.
virtual bool DrawBinormals(
ozz::span<const float> _positions, size_t _positions_stride,
ozz::span<const float> _normals, size_t _normals_stride,
ozz::span<const float> _tangents, size_t _tangents_stride,
ozz::span<const float> _handenesses, size_t _handenesses_stride,
int _num_vectors, float _vector_length, Color _color,
const ozz::math::Float4x4& _transform) = 0;
};
} // namespace sample
} // namespace ozz
#endif // OZZ_SAMPLES_FRAMEWORK_RENDERER_H_
@@ -0,0 +1,57 @@
# Adds fbx2mesh utility target.
if(ozz_build_fbx)
# share meshes with thte sample framework
add_executable(sample_fbx2mesh
fbx2mesh.cc
${PROJECT_SOURCE_DIR}/samples/framework/mesh.cc
${PROJECT_SOURCE_DIR}/samples/framework/mesh.h)
target_link_libraries(sample_fbx2mesh
ozz_animation_fbx
ozz_options)
set_target_properties(sample_fbx2mesh
PROPERTIES FOLDER "samples/tools")
install(TARGETS sample_fbx2mesh DESTINATION bin/samples/tools)
add_custom_command(
DEPENDS $<$<BOOL:${ozz_build_fbx}>:BUILD_DATA>
$<$<AND:$<BOOL:${ozz_build_data}>,$<BOOL:${ozz_build_fbx}>>:sample_fbx2mesh>
"${ozz_media_directory}/bin/pab_skeleton.ozz"
"${ozz_media_directory}/fbx/pab/arnaud.fbx"
"${ozz_media_directory}/fbx/sketchfab/ruby.fbx"
"${ozz_media_directory}/collada/floor.dae"
OUTPUT "${ozz_media_directory}/bin/arnaud_mesh.ozz"
"${ozz_media_directory}/bin/arnaud_mesh_4.ozz"
OUTPUT "${ozz_media_directory}/bin/ruby_mesh.ozz"
"${ozz_media_directory}/bin/floor.ozz"
COMMAND sample_fbx2mesh "--file=${ozz_media_directory}/fbx/pab/arnaud.fbx" "--skeleton=${ozz_media_directory}/bin/pab_skeleton.ozz" "--mesh=${ozz_media_directory}/bin/arnaud_mesh.ozz"
COMMAND sample_fbx2mesh "--file=${ozz_media_directory}/fbx/pab/arnaud.fbx" "--skeleton=${ozz_media_directory}/bin/pab_skeleton.ozz" "--mesh=${ozz_media_directory}/bin/arnaud_mesh_4.ozz" --nosplit --max_influences=4
COMMAND sample_fbx2mesh "--file=${ozz_media_directory}/fbx/sketchfab/ruby.fbx" "--skeleton=${ozz_media_directory}/bin/ruby_skeleton.ozz" "--mesh=${ozz_media_directory}/bin/ruby_mesh.ozz" --max_influences=4
COMMAND sample_fbx2mesh "--file=${ozz_media_directory}/collada/floor.dae" "--mesh=${ozz_media_directory}/bin/floor.ozz" "--skeleton=${ozz_media_directory}/bin/pab_skeleton.ozz"
VERBATIM)
# Creates a target to build sample data
add_custom_target(BUILD_DATA_SAMPLE ALL DEPENDS
"${ozz_media_directory}/bin/arnaud_mesh.ozz"
"${ozz_media_directory}/bin/arnaud_mesh_4.ozz"
"${ozz_media_directory}/bin/ruby_mesh.ozz"
"${ozz_media_directory}/bin/floor.ozz"
VERBATIM)
add_test(NAME sample_fbx2mesh COMMAND sample_fbx2mesh "--file=${ozz_media_directory}/fbx/pab/skeleton.fbx" "--skeleton=${ozz_media_directory}/bin/pab_skeleton.ozz" "--mesh=${ozz_temp_directory}/mesh.ozz")
add_test(NAME sample_fbx2mesh_invalid_file COMMAND sample_fbx2mesh "--file=${ozz_temp_directory}/dont_exist.fbx" "--skeleton=${ozz_media_directory}/bin/pab_skeleton.ozz" "--mesh=${ozz_temp_directory}/should_not_exist.ozz")
set_tests_properties(sample_fbx2mesh_invalid_file PROPERTIES WILL_FAIL true)
add_test(NAME sample_fbx2mesh_invalid_skeleton COMMAND sample_fbx2mesh "--file=${ozz_media_directory}/fbx/pab/skeleton.fbx" "--skeleton=${ozz_media_directory}/bin/pab_walk.ozz" "--mesh=${ozz_temp_directory}/should_not_exist.ozz")
set_tests_properties(sample_fbx2mesh_invalid_skeleton PROPERTIES WILL_FAIL true)
# Ensures nothing was outputted.
add_test(NAME sample_fbx2mesh_output COMMAND ${CMAKE_COMMAND} -E copy "${ozz_temp_directory}/should_not_exist.ozz" "${ozz_temp_directory}/should_not_exist_too.ozz")
set_tests_properties(sample_fbx2mesh_output PROPERTIES WILL_FAIL true)
set_tests_properties(sample_fbx2mesh_output PROPERTIES
DEPENDS "sample_fbx2mesh_invalid_file
sample_fbx2mesh_invalid_skeleton")
endif()
File diff suppressed because it is too large Load Diff
+544
View File
@@ -0,0 +1,544 @@
//----------------------------------------------------------------------------//
// //
// ozz-animation is hosted at http://github.com/guillaumeblanc/ozz-animation //
// and distributed under the MIT License (MIT). //
// //
// Copyright (c) Guillaume Blanc //
// //
// Permission is hereby granted, free of charge, to any person obtaining a //
// copy of this software and associated documentation files (the "Software"), //
// to deal in the Software without restriction, including without limitation //
// the rights to use, copy, modify, merge, publish, distribute, sublicense, //
// and/or sell copies of the Software, and to permit persons to whom the //
// Software is furnished to do so, subject to the following conditions: //
// //
// The above copyright notice and this permission notice shall be included in //
// all copies or substantial portions of the Software. //
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL //
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING //
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER //
// DEALINGS IN THE SOFTWARE. //
// //
//----------------------------------------------------------------------------//
#include "framework/utils.h"
#include <cassert>
#include <limits>
#include "ozz/base/maths/box.h"
#include "ozz/base/maths/simd_math.h"
#include "ozz/base/maths/simd_quaternion.h"
#include "ozz/base/maths/soa_transform.h"
#include "ozz/base/memory/allocator.h"
#include "ozz/animation/runtime/animation.h"
#include "ozz/animation/runtime/local_to_model_job.h"
#include "ozz/animation/runtime/skeleton.h"
#include "ozz/animation/runtime/track.h"
#include "ozz/animation/offline/raw_skeleton.h"
#include "ozz/geometry/runtime/skinning_job.h"
#include "ozz/base/io/archive.h"
#include "ozz/base/io/stream.h"
#include "ozz/base/log.h"
#include "framework/imgui.h"
#include "framework/mesh.h"
namespace ozz {
namespace sample {
PlaybackController::PlaybackController()
: time_ratio_(0.f),
previous_time_ratio_(0.f),
playback_speed_(1.f),
play_(true),
loop_(true) {}
void PlaybackController::Update(const animation::Animation& _animation,
float _dt) {
float new_time = time_ratio_;
if (play_) {
new_time = time_ratio_ + _dt * playback_speed_ / _animation.duration();
}
// Must be called even if time doesn't change, in order to update previous
// frame time ratio. Uses set_time_ratio function in order to update
// previous_time_ an wrap time value in the unit interval (depending on loop
// mode).
set_time_ratio(new_time);
}
void PlaybackController::set_time_ratio(float _ratio) {
previous_time_ratio_ = time_ratio_;
if (loop_) {
// Wraps in the unit interval [0:1], even for negative values (the reason
// for using floorf).
time_ratio_ = _ratio - floorf(_ratio);
} else {
// Clamps in the unit interval [0:1].
time_ratio_ = math::Clamp(0.f, _ratio, 1.f);
}
}
// Gets animation current time.
float PlaybackController::time_ratio() const { return time_ratio_; }
// Gets animation time of last update.
float PlaybackController::previous_time_ratio() const {
return previous_time_ratio_;
}
void PlaybackController::Reset() {
previous_time_ratio_ = time_ratio_ = 0.f;
playback_speed_ = 1.f;
play_ = true;
}
bool PlaybackController::OnGui(const animation::Animation& _animation,
ImGui* _im_gui, bool _enabled,
bool _allow_set_time) {
bool time_changed = false;
if (_im_gui->DoButton(play_ ? "Pause" : "Play", _enabled)) {
play_ = !play_;
}
_im_gui->DoCheckBox("Loop", &loop_, _enabled);
char szLabel[64];
// Uses a local copy of time_ so that set_time is used to actually apply
// changes. Otherwise previous time would be incorrect.
float ratio = time_ratio();
std::sprintf(szLabel, "Animation time: %.2f", ratio * _animation.duration());
if (_im_gui->DoSlider(szLabel, 0.f, 1.f, &ratio, 1.f,
_enabled && _allow_set_time)) {
set_time_ratio(ratio);
// Pause the time if slider as moved.
play_ = false;
time_changed = true;
}
std::sprintf(szLabel, "Playback speed: %.2f", playback_speed_);
_im_gui->DoSlider(szLabel, -5.f, 5.f, &playback_speed_, 1.f, _enabled);
// Allow to reset speed if it is not the default value.
if (_im_gui->DoButton("Reset playback speed",
playback_speed_ != 1.f && _enabled)) {
playback_speed_ = 1.f;
}
return time_changed;
}
namespace {
bool OnRawSkeletonJointGui(
ozz::sample::ImGui* _im_gui,
ozz::animation::offline::RawSkeleton::Joint::Children* _children,
ozz::vector<bool>::iterator* _oc_state) {
char txt[255];
bool modified = false;
for (size_t i = 0; i < _children->size(); ++i) {
ozz::animation::offline::RawSkeleton::Joint& joint = _children->at(i);
bool opened = *(*_oc_state);
ozz::sample::ImGui::OpenClose oc(_im_gui, joint.name.c_str(), &opened);
*(*_oc_state)++ = opened; // Updates state and increment for next joint.
if (opened) {
// Translation
ozz::math::Float3& translation = joint.transform.translation;
_im_gui->DoLabel("Translation");
sprintf(txt, "x %.2g", translation.x);
modified |= _im_gui->DoSlider(txt, -1.f, 1.f, &translation.x);
sprintf(txt, "y %.2g", translation.y);
modified |= _im_gui->DoSlider(txt, -1.f, 1.f, &translation.y);
sprintf(txt, "z %.2g", translation.z);
modified |= _im_gui->DoSlider(txt, -1.f, 1.f, &translation.z);
// Rotation (in euler form)
ozz::math::Quaternion& rotation = joint.transform.rotation;
_im_gui->DoLabel("Rotation");
ozz::math::Float3 euler = ToEuler(rotation) * ozz::math::kRadianToDegree;
sprintf(txt, "x %.3g", euler.x);
bool euler_modified = _im_gui->DoSlider(txt, -180.f, 180.f, &euler.x);
sprintf(txt, "y %.3g", euler.y);
euler_modified |= _im_gui->DoSlider(txt, -180.f, 180.f, &euler.y);
sprintf(txt, "z %.3g", euler.z);
euler_modified |= _im_gui->DoSlider(txt, -180.f, 180.f, &euler.z);
if (euler_modified) {
modified = true;
ozz::math::Float3 euler_rad = euler * ozz::math::kDegreeToRadian;
rotation = ozz::math::Quaternion::FromEuler(euler_rad.x, euler_rad.y,
euler_rad.z);
}
// Scale (must be uniform and not 0)
_im_gui->DoLabel("Scale");
ozz::math::Float3& scale = joint.transform.scale;
sprintf(txt, "%.2g", scale.x);
if (_im_gui->DoSlider(txt, -1.f, 1.f, &scale.x)) {
modified = true;
scale.y = scale.z = scale.x = scale.x != 0.f ? scale.x : .01f;
}
// Recurse children
modified |= OnRawSkeletonJointGui(_im_gui, &joint.children, _oc_state);
}
}
return modified;
}
} // namespace
bool RawSkeletonEditor::OnGui(animation::offline::RawSkeleton* _skeleton,
ImGui* _im_gui) {
open_close_states.resize(_skeleton->num_joints(), false);
ozz::vector<bool>::iterator begin = open_close_states.begin();
return OnRawSkeletonJointGui(_im_gui, &_skeleton->roots, &begin);
}
// Uses LocalToModelJob to compute skeleton model space posture, then forwards
// to ComputePostureBounds
void ComputeSkeletonBounds(const animation::Skeleton& _skeleton,
math::Box* _bound) {
using ozz::math::Float4x4;
assert(_bound);
// Set a default box.
*_bound = ozz::math::Box();
const int num_joints = _skeleton.num_joints();
if (!num_joints) {
return;
}
// Allocate matrix array, out of memory is handled by the LocalToModelJob.
ozz::vector<ozz::math::Float4x4> models(num_joints);
// Compute model space bind pose.
ozz::animation::LocalToModelJob job;
job.input = _skeleton.joint_bind_poses();
job.output = make_span(models);
job.skeleton = &_skeleton;
if (job.Run()) {
// Forwards to posture function.
ComputePostureBounds(job.output, _bound);
}
}
// Loop through matrices and collect min and max bounds.
void ComputePostureBounds(ozz::span<const ozz::math::Float4x4> _matrices,
math::Box* _bound) {
assert(_bound);
// Set a default box.
*_bound = ozz::math::Box();
if (_matrices.empty()) {
return;
}
// Loops through matrices and stores min/max.
// Matrices array cannot be empty, it was checked at the beginning of the
// function.
const ozz::math::Float4x4* current = _matrices.begin();
math::SimdFloat4 min = current->cols[3];
math::SimdFloat4 max = current->cols[3];
++current;
while (current < _matrices.end()) {
min = math::Min(min, current->cols[3]);
max = math::Max(max, current->cols[3]);
++current;
}
// Stores in math::Box structure.
math::Store3PtrU(min, &_bound->min.x);
math::Store3PtrU(max, &_bound->max.x);
return;
}
void MultiplySoATransformQuaternion(
int _index, const ozz::math::SimdQuaternion& _quat,
const ozz::span<ozz::math::SoaTransform>& _transforms) {
assert(_index >= 0 && static_cast<size_t>(_index) < _transforms.size() * 4 &&
"joint index out of bound.");
// Convert soa to aos in order to perform quaternion multiplication, and gets
// back to soa.
ozz::math::SoaTransform& soa_transform_ref = _transforms[_index / 4];
ozz::math::SimdQuaternion aos_quats[4];
ozz::math::Transpose4x4(&soa_transform_ref.rotation.x, &aos_quats->xyzw);
ozz::math::SimdQuaternion& aos_quat_ref = aos_quats[_index & 3];
aos_quat_ref = aos_quat_ref * _quat;
ozz::math::Transpose4x4(&aos_quats->xyzw, &soa_transform_ref.rotation.x);
}
bool LoadSkeleton(const char* _filename, ozz::animation::Skeleton* _skeleton) {
assert(_filename && _skeleton);
ozz::log::Out() << "Loading skeleton archive " << _filename << "."
<< std::endl;
ozz::io::File file(_filename, "rb");
if (!file.opened()) {
ozz::log::Err() << "Failed to open skeleton file " << _filename << "."
<< std::endl;
return false;
}
ozz::io::IArchive archive(&file);
if (!archive.TestTag<ozz::animation::Skeleton>()) {
ozz::log::Err() << "Failed to load skeleton instance from file "
<< _filename << "." << std::endl;
return false;
}
// Once the tag is validated, reading cannot fail.
archive >> *_skeleton;
return true;
}
bool LoadAnimation(const char* _filename,
ozz::animation::Animation* _animation) {
assert(_filename && _animation);
ozz::log::Out() << "Loading animation archive: " << _filename << "."
<< std::endl;
ozz::io::File file(_filename, "rb");
if (!file.opened()) {
ozz::log::Err() << "Failed to open animation file " << _filename << "."
<< std::endl;
return false;
}
ozz::io::IArchive archive(&file);
if (!archive.TestTag<ozz::animation::Animation>()) {
ozz::log::Err() << "Failed to load animation instance from file "
<< _filename << "." << std::endl;
return false;
}
// Once the tag is validated, reading cannot fail.
archive >> *_animation;
return true;
}
namespace {
template <typename _Track>
bool LoadTrackImpl(const char* _filename, _Track* _track) {
assert(_filename && _track);
ozz::log::Out() << "Loading track archive: " << _filename << "." << std::endl;
ozz::io::File file(_filename, "rb");
if (!file.opened()) {
ozz::log::Err() << "Failed to open track file " << _filename << "."
<< std::endl;
return false;
}
ozz::io::IArchive archive(&file);
if (!archive.TestTag<_Track>()) {
ozz::log::Err() << "Failed to load float track instance from file "
<< _filename << "." << std::endl;
return false;
}
// Once the tag is validated, reading cannot fail.
archive >> *_track;
return true;
}
} // namespace
bool LoadTrack(const char* _filename, ozz::animation::FloatTrack* _track) {
return LoadTrackImpl(_filename, _track);
}
bool LoadTrack(const char* _filename, ozz::animation::Float2Track* _track) {
return LoadTrackImpl(_filename, _track);
}
bool LoadTrack(const char* _filename, ozz::animation::Float3Track* _track) {
return LoadTrackImpl(_filename, _track);
}
bool LoadTrack(const char* _filename, ozz::animation::Float4Track* _track) {
return LoadTrackImpl(_filename, _track);
}
bool LoadTrack(const char* _filename, ozz::animation::QuaternionTrack* _track) {
return LoadTrackImpl(_filename, _track);
}
bool LoadMesh(const char* _filename, ozz::sample::Mesh* _mesh) {
assert(_filename && _mesh);
ozz::log::Out() << "Loading mesh archive: " << _filename << "." << std::endl;
ozz::io::File file(_filename, "rb");
if (!file.opened()) {
ozz::log::Err() << "Failed to open mesh file " << _filename << "."
<< std::endl;
return false;
}
ozz::io::IArchive archive(&file);
if (!archive.TestTag<ozz::sample::Mesh>()) {
ozz::log::Err() << "Failed to load mesh instance from file " << _filename
<< "." << std::endl;
return false;
}
// Once the tag is validated, reading cannot fail.
archive >> *_mesh;
return true;
}
bool LoadMeshes(const char* _filename,
ozz::vector<ozz::sample::Mesh>* _meshes) {
assert(_filename && _meshes);
ozz::log::Out() << "Loading meshes archive: " << _filename << "."
<< std::endl;
ozz::io::File file(_filename, "rb");
if (!file.opened()) {
ozz::log::Err() << "Failed to open mesh file " << _filename << "."
<< std::endl;
return false;
}
ozz::io::IArchive archive(&file);
while (archive.TestTag<ozz::sample::Mesh>()) {
_meshes->resize(_meshes->size() + 1);
archive >> _meshes->back();
}
return true;
}
namespace {
// MollerTrumbore intersection algorithm
// https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm
bool RayIntersectsTriangle(const ozz::math::Float3& _ray_origin,
const ozz::math::Float3& _ray_direction,
const ozz::math::Float3& _p0,
const ozz::math::Float3& _p1,
const ozz::math::Float3& _p2,
ozz::math::Float3* _intersect,
ozz::math::Float3* _normal) {
const float kEpsilon = 0.0000001f;
const ozz::math::Float3 edge1 = _p1 - _p0;
const ozz::math::Float3 edge2 = _p2 - _p0;
const ozz::math::Float3 h = Cross(_ray_direction, edge2);
const float a = Dot(edge1, h);
if (a > -kEpsilon && a < kEpsilon) {
return false; // This ray is parallel to this triangle.
}
const float inv_a = 1.f / a;
const ozz::math::Float3 s = _ray_origin - _p0;
const float u = Dot(s, h) * inv_a;
if (u < 0.f || u > 1.f) {
return false;
}
const ozz::math::Float3 q = Cross(s, edge1);
const float v = ozz::math::Dot(_ray_direction, q) * inv_a;
if (v < 0.f || u + v > 1.f) {
return false;
}
// At this stage we can compute t to find out where the intersection point is
// on the line.
const float t = Dot(edge2, q) * inv_a;
if (t > kEpsilon) { // Ray intersection
*_intersect = _ray_origin + _ray_direction * t;
*_normal = Normalize(Cross(edge1, edge2));
return true;
} else { // This means that there is a line intersection but not a ray
// intersection.
return false;
}
}
} // namespace
bool RayIntersectsMesh(const ozz::math::Float3& _ray_origin,
const ozz::math::Float3& _ray_direction,
const ozz::sample::Mesh& _mesh,
ozz::math::Float3* _intersect,
ozz::math::Float3* _normal) {
assert(_mesh.parts.size() == 1 && !_mesh.skinned());
bool intersected = false;
ozz::math::Float3 intersect, normal;
const float* vertices = array_begin(_mesh.parts[0].positions);
const uint16_t* indices = array_begin(_mesh.triangle_indices);
for (int i = 0; i < _mesh.triangle_index_count(); i += 3) {
const float* pf0 = vertices + indices[i + 0] * 3;
const float* pf1 = vertices + indices[i + 1] * 3;
const float* pf2 = vertices + indices[i + 2] * 3;
ozz::math::Float3 lcl_intersect, lcl_normal;
if (RayIntersectsTriangle(_ray_origin, _ray_direction,
ozz::math::Float3(pf0[0], pf0[1], pf0[2]),
ozz::math::Float3(pf1[0], pf1[1], pf1[2]),
ozz::math::Float3(pf2[0], pf2[1], pf2[2]),
&lcl_intersect, &lcl_normal)) {
// Is it closer to start point than the previous intersection.
if (!intersected || LengthSqr(lcl_intersect - _ray_origin) <
LengthSqr(intersect - _ray_origin)) {
intersect = lcl_intersect;
normal = lcl_normal;
}
intersected = true;
}
}
// Copy output
if (intersected) {
if (_intersect) {
*_intersect = intersect;
}
if (_normal) {
*_normal = normal;
}
}
return intersected;
}
bool RayIntersectsMeshes(const ozz::math::Float3& _ray_origin,
const ozz::math::Float3& _ray_direction,
const ozz::span<const ozz::sample::Mesh>& _meshes,
ozz::math::Float3* _intersect,
ozz::math::Float3* _normal) {
bool intersected = false;
ozz::math::Float3 intersect, normal;
for (size_t i = 0; i < _meshes.size(); ++i) {
ozz::math::Float3 lcl_intersect, lcl_normal;
if (RayIntersectsMesh(_ray_origin, _ray_direction, _meshes[i],
&lcl_intersect, &lcl_normal)) {
// Is it closer to start point than the previous intersection.
if (!intersected || LengthSqr(lcl_intersect - _ray_origin) <
LengthSqr(intersect - _ray_origin)) {
intersect = lcl_intersect;
normal = lcl_normal;
}
intersected = true;
}
}
// Copy output
if (intersected) {
if (_intersect) {
*_intersect = intersect;
}
if (_normal) {
*_normal = normal;
}
}
return intersected;
}
} // namespace sample
} // namespace ozz
+214
View File
@@ -0,0 +1,214 @@
//----------------------------------------------------------------------------//
// //
// ozz-animation is hosted at http://github.com/guillaumeblanc/ozz-animation //
// and distributed under the MIT License (MIT). //
// //
// Copyright (c) Guillaume Blanc //
// //
// Permission is hereby granted, free of charge, to any person obtaining a //
// copy of this software and associated documentation files (the "Software"), //
// to deal in the Software without restriction, including without limitation //
// the rights to use, copy, modify, merge, publish, distribute, sublicense, //
// and/or sell copies of the Software, and to permit persons to whom the //
// Software is furnished to do so, subject to the following conditions: //
// //
// The above copyright notice and this permission notice shall be included in //
// all copies or substantial portions of the Software. //
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL //
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING //
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER //
// DEALINGS IN THE SOFTWARE. //
// //
//----------------------------------------------------------------------------//
#ifndef OZZ_SAMPLES_FRAMEWORK_UTILS_H_
#define OZZ_SAMPLES_FRAMEWORK_UTILS_H_
#include "ozz/base/containers/vector.h"
#include "ozz/base/platform.h"
#include "ozz/base/span.h"
namespace ozz {
// Forward declarations.
namespace math {
struct Box;
struct Float3;
struct Float4x4;
struct SimdQuaternion;
struct SoaTransform;
} // namespace math
namespace animation {
class Animation;
class Skeleton;
class FloatTrack;
class Float2Track;
class Float3Track;
class Float4Track;
class QuaternionTrack;
namespace offline {
struct RawAnimation;
struct RawSkeleton;
} // namespace offline
} // namespace animation
namespace sample {
class ImGui;
struct Mesh;
// Utility class that helps with controlling animation playback time. Time is
// computed every update according to the dt given by the caller, playback speed
// and "play" state.
// Internally time is stored as a ratio in unit interval [0,1], as expected by
// ozz runtime animation jobs.
// OnGui function allows to tweak controller parameters through the application
// Gui.
class PlaybackController {
public:
// Constructor.
PlaybackController();
// Sets animation current time.
void set_time_ratio(float _time);
// Gets animation current time.
float time_ratio() const;
// Gets animation time ratio of last update. Useful when the range between
// previous and current frame needs to pe processed.
float previous_time_ratio() const;
// Sets playback speed.
void set_playback_speed(float _speed) { playback_speed_ = _speed; }
// Gets playback speed.
float playback_speed() const { return playback_speed_; }
// Sets loop modes. If true, animation time is always clamped between 0 and 1.
void set_loop(bool _loop) { loop_ = _loop; }
// Gets loop mode.
bool loop() const { return loop_; }
// Updates animation time if in "play" state, according to playback speed and
// given frame time _dt.
// Returns true if animation has looped during update
void Update(const animation::Animation& _animation, float _dt);
// Resets all parameters to their default value.
void Reset();
// Do controller Gui.
// Returns true if animation time has been changed.
bool OnGui(const animation::Animation& _animation, ImGui* _im_gui,
bool _enabled = true, bool _allow_set_time = true);
private:
// Current animation time ratio, in the unit interval [0,1], where 0 is the
// beginning of the animation, 1 is the end.
float time_ratio_;
// Time ratio of the previous update.
float previous_time_ratio_;
// Playback speed, can be negative in order to play the animation backward.
float playback_speed_;
// Animation play mode state: play/pause.
bool play_;
// Animation loop mode.
bool loop_;
};
// Computes the bounding box of _skeleton. This is the box that encloses all
// skeleton's joints in model space.
// _bound must be a valid math::Box instance.
void ComputeSkeletonBounds(const animation::Skeleton& _skeleton,
math::Box* _bound);
// Computes the bounding box of posture defines be _matrices range.
// _bound must be a valid math::Box instance.
void ComputePostureBounds(ozz::span<const ozz::math::Float4x4> _matrices,
math::Box* _bound);
// Allows to edit translation/rotation/scale of a skeleton pose.
// This object should be used for a single skeleton, because it stores
// open/close states from a frame to the next.
class RawSkeletonEditor {
public:
// Returns true if skeleton was modified.
bool OnGui(animation::offline::RawSkeleton* _skeleton, ImGui* _im_gui);
private:
// Imgui Open/Close states for each skeleton joint.
ozz::vector<bool> open_close_states;
};
// Multiplies a single quaternion at a specific index in a SoA transform range.
void MultiplySoATransformQuaternion(
int _index, const ozz::math::SimdQuaternion& _quat,
const ozz::span<ozz::math::SoaTransform>& _transforms);
// Loads a skeleton from an ozz archive file named _filename.
// This function will fail and return false if the file cannot be opened or if
// it is not a valid ozz skeleton archive. A valid skeleton archive can be
// produced with ozz tools (fbx2ozz) or using ozz skeleton serialization API.
// _filename and _skeleton must be non-nullptr.
bool LoadSkeleton(const char* _filename, ozz::animation::Skeleton* _skeleton);
// Loads an animation from an ozz archive file named _filename.
// This function will fail and return false if the file cannot be opened or if
// it is not a valid ozz animation archive. A valid animation archive can be
// produced with ozz tools (fbx2ozz) or using ozz animation serialization API.
// _filename and _animation must be non-nullptr.
bool LoadAnimation(const char* _filename,
ozz::animation::Animation* _animation);
// Loads a float track from an ozz archive file named _filename.
// This function will fail and return false if the file cannot be opened or if
// it is not a valid ozz float track archive. A valid float track archive can be
// produced with ozz tools (fbx2ozz) or using ozz serialization API.
// _filename and _track must be non-nullptr.
bool LoadTrack(const char* _filename, ozz::animation::FloatTrack* _track);
bool LoadTrack(const char* _filename, ozz::animation::Float2Track* _track);
bool LoadTrack(const char* _filename, ozz::animation::Float3Track* _track);
bool LoadTrack(const char* _filename, ozz::animation::Float4Track* _track);
bool LoadTrack(const char* _filename, ozz::animation::QuaternionTrack* _track);
// Loads a sample::Mesh from an ozz archive file named _filename.
// This function will fail and return false if the file cannot be opened or if
// it is not a valid ozz mesh archive. A valid mesh archive can be
// serialization API.
// _filename and _mesh must be non-nullptr.
bool LoadMesh(const char* _filename, ozz::sample::Mesh* _mesh);
// Loads n sample::Mesh from an ozz archive file named _filename.
// This function will fail and return false if the file cannot be opened or if
// it is not a valid ozz mesh archive. A valid mesh archive can be
// produced with ozz tools (fbx2skin) or using ozz animation serialization API.
// _filename and _mesh must be non-nullptr.
bool LoadMeshes(const char* _filename, ozz::vector<ozz::sample::Mesh>* _meshes);
// Intersect _mesh with the half-line extending from _ray_origin indefinitely in
// _ray_direction only. Returns true if there was an intersection. Fills
// intersection point and normal if provided, with the closest intersecting
// triangle from _ray_origin. Only supports non-skinned, single part meshes.
bool RayIntersectsMesh(const ozz::math::Float3& _ray_origin,
const ozz::math::Float3& _ray_direction,
const ozz::sample::Mesh& _mesh,
ozz::math::Float3* _intersect,
ozz::math::Float3* _normal);
// Intersect _meshes with the half-line extending from _ray_origin indefinitely
// in _ray_direction only. See RayIntersectsMesh.
bool RayIntersectsMeshes(const ozz::math::Float3& _ray_origin,
const ozz::math::Float3& _ray_direction,
const ozz::span<const ozz::sample::Mesh>& _meshes,
ozz::math::Float3* _intersect,
ozz::math::Float3* _normal);
} // namespace sample
} // namespace ozz
#endif // OZZ_SAMPLES_FRAMEWORK_UTILS_H_