Initial commit
This commit is contained in:
@@ -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)
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -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
@@ -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_
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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 {
|
||||
// Moller–Trumbore 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
@@ -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_
|
||||
Reference in New Issue
Block a user