Updated ozz-animation to version 0.14.1 @35b2efd4

This commit is contained in:
Martin Felis
2023-03-26 13:28:12 +02:00
parent bf3189ff49
commit 15871f349c
194 changed files with 3495 additions and 1957 deletions
+2 -5
View File
@@ -29,9 +29,7 @@ add_library(sample_framework STATIC
# Samples requires OpenGL package.
if(NOT EMSCRIPTEN)
add_subdirectory(${PROJECT_SOURCE_DIR}/extern/glfw glfw)
target_link_libraries(sample_framework
glfw)
target_link_libraries(sample_framework glfw)
endif()
target_link_libraries(sample_framework
@@ -43,7 +41,6 @@ if(TARGET BUILD_DATA_SAMPLE)
add_dependencies(sample_framework BUILD_DATA_SAMPLE)
endif()
set_target_properties(sample_framework
PROPERTIES FOLDER "samples")
set_target_properties(sample_framework PROPERTIES FOLDER "samples")
add_subdirectory(tools)
+48
View File
@@ -476,6 +476,12 @@ bool Application::Gui() {
// Downcast to public imgui.
ImGui* im_gui = im_gui_.get();
// Do floating gui.
if (!show_help_) {
success = OnFloatingGui(im_gui);
}
// Help gui.
{
math::RectFloat rect(kGuiMargin, kGuiMargin,
@@ -658,6 +664,31 @@ bool Application::FrameworkGui() {
return true;
}
bool Application::OnInitialize() { return true; }
void Application::OnDestroy() {}
bool Application::OnUpdate(float _dt, float _time) {
(void)_dt;
(void)_time;
return true;
}
bool Application::OnGui(ImGui* _im_gui) {
(void)_im_gui;
return true;
}
bool Application::OnFloatingGui(ImGui* _im_gui) {
(void)_im_gui;
return true;
}
bool Application::OnDisplay(Renderer* _renderer) {
(void)_renderer;
return true;
}
bool Application::GetCameraInitialSetup(math::Float3* _center,
math::Float2* _angles,
float* _distance) const {
@@ -674,6 +705,23 @@ bool Application::GetCameraOverride(math::Float4x4* _transform) const {
return false;
}
void Application::GetSceneBounds(math::Box* _bound) const { (void)_bound; }
math::Float2 Application::WorldToScreen(const math::Float3& _world) const {
const math::SimdFloat4 ndc =
(camera_->projection() * camera_->view()) *
math::simd_float4::Load(_world.x, _world.y, _world.z, 1.f);
const math::SimdFloat4 resolution = math::simd_float4::FromInt(
math::simd_int4::Load(resolution_.width, resolution_.height, 0, 0));
const ozz::math::SimdFloat4 screen =
resolution * ((ndc / math::SplatW(ndc)) + math::simd_float4::one()) /
math::simd_float4::Load1(2.f);
math::Float2 ret;
math::Store2PtrU(screen, &ret.x);
return ret;
}
void Application::ResizeCbk(int _width, int _height) {
// Stores new resolution settings.
application_->resolution_.width = _width;
+18 -6
View File
@@ -29,6 +29,7 @@
#define OZZ_SAMPLES_FRAMEWORK_APPLICATION_H_
#include <cstddef>
#include "ozz/base/containers/string.h"
#include "ozz/base/memory/unique_ptr.h"
@@ -78,36 +79,47 @@ class Application {
int Run(int _argc, const char** _argv, const char* _version,
const char* _title);
protected:
// Allows application to convert from world space to screen coordinates.
math::Float2 WorldToScreen(const math::Float3& _world) const;
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;
virtual bool OnInitialize();
// Provides de-initialization event to the inheriting application.
// OnDestroy is called even if OnInitialize failed and returned an error.
virtual void OnDestroy() = 0;
virtual void OnDestroy();
// 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;
virtual bool OnUpdate(float _dt, float _time);
// 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;
virtual bool OnGui(ImGui* _im_gui);
// Provides immediate mode floating gui display event to the inheriting
// application. Floating gui allows to render a Gui anywere one screen. User
// must provide the form. OnFloatingGui 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 OnFloatingGui(ImGui* _im_gui);
// 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;
virtual bool OnDisplay(Renderer* _renderer);
// Initial camera values. These will only be considered if function returns
// true;
@@ -125,7 +137,7 @@ class Application {
// 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;
virtual void GetSceneBounds(math::Box* _bound) const;
// Implements framework internal loop function.
bool Loop();
+14 -6
View File
@@ -28,12 +28,14 @@
#ifndef OZZ_SAMPLES_FRAMEWORK_IMGUI_H_
#define OZZ_SAMPLES_FRAMEWORK_IMGUI_H_
#include <ozz/base/containers/array.h>
#include <cstdio>
namespace ozz {
namespace math {
struct RectFloat;
}
} // namespace math
namespace sample {
// Interface for immediate mode graphical user interface rendering.
@@ -54,8 +56,9 @@ class ImGui {
// 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.
// 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,
@@ -115,6 +118,11 @@ class ImGui {
float* _value, float _pow = 1.f,
bool _enabled = true) = 0;
virtual bool DoSlider2D(const char* _label, ozz::array<float, 2> _min,
ozz::array<float, 2> _max,
ozz::array<float, 2>* _value,
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
@@ -172,9 +180,9 @@ class ImGui {
// 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.
// 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;
@@ -34,11 +34,11 @@
#include <cstdio>
#include <cstring>
#include "immediate.h"
#include "ozz/base/maths/math_constant.h"
#include "ozz/base/maths/math_ex.h"
#include "ozz/base/maths/vec_float.h"
#include "ozz/base/memory/allocator.h"
#include "immediate.h"
#include "renderer_impl.h"
namespace ozz {
@@ -211,6 +211,11 @@ bool ImGuiImpl::AddWidget(float _height, math::RectFloat* _rect) {
// Get current container.
Container& container = containers_.back();
// Make it a square if height is negative.
if (_height < 0) {
_height = container.rect.width - kWidgetMarginX * 2.f;
}
// Early out if outside of the container.
// But don't modify current container's state.
if (container.offset_y < kWidgetMarginY + _height) {
@@ -740,6 +745,33 @@ void ImGuiImpl::DoGraph(const char* _label, float _min, float _max, float _mean,
}
}
namespace {
float SliderControl(float _value, float _min, float _max, float _pow,
int _mouse, int _mouse_max, bool _enabled, bool _active,
float* _cursor) {
const float pow_min = powf(_min, _pow);
const float pow_max = powf(_max, _pow);
const float clamped_value = ozz::math::Clamp(_min, _value, _max);
float pow_value = powf(clamped_value, _pow);
// Finds cursor position and rect.
*_cursor = floorf((_mouse_max * (pow_value - pow_min)) / (pow_max - pow_min));
if (_enabled) {
if (_active) {
_mouse = ozz::math::Clamp(0, _mouse, _mouse_max);
pow_value = (_mouse * (pow_max - pow_min)) / _mouse_max + pow_min;
return ozz::math::Clamp(_min, powf(pow_value, 1.f / _pow), _max);
} else {
// Clamping is only applied if the widget is enabled.
return clamped_value;
}
}
return _value;
}
} // namespace
bool ImGuiImpl::DoSlider(const char* _label, float _min, float _max,
float* _value, float _pow, bool _enabled) {
math::RectFloat rect;
@@ -751,7 +783,6 @@ bool ImGuiImpl::DoSlider(const char* _label, float _min, float _max,
// Calculate mouse cursor's relative y offset.
const float initial_value = *_value;
const float clamped_value = ozz::math::Clamp(_min, initial_value, _max);
// Check for hotness.
bool hot = false, active = false;
@@ -795,24 +826,100 @@ bool ImGuiImpl::DoSlider(const char* _label, float _min, float _max,
}
// Update widget value.
const float pow_min = powf(_min, _pow);
const float pow_max = powf(_max, _pow);
float pow_value = powf(clamped_value, _pow);
float cursor;
*_value =
SliderControl(initial_value, _min, _max, _pow,
inputs_.mouse_x - static_cast<int>(rect.left),
static_cast<int>(rect.width), _enabled, active, &cursor);
// Renders slider's rail.
const math::RectFloat rail_rect(rect.left, rect.bottom, rect.width,
rect.height);
FillRect(rail_rect, kSliderRoundRectRadius, background_color);
StrokeRect(rail_rect, kSliderRoundRectRadius, border_color);
const math::RectFloat cursor_rect(
rect.left + cursor - kWidgetCursorWidth / 2.f, rect.bottom - 1.f,
kWidgetCursorWidth, rect.height + 2.f);
FillRect(cursor_rect, kSliderRoundRectRadius, slider_color);
StrokeRect(cursor_rect, kSliderRoundRectRadius, slider_border_color);
const math::RectFloat text_rect(
rail_rect.left + kSliderRoundRectRadius, rail_rect.bottom,
rail_rect.width - kSliderRoundRectRadius * 2.f, rail_rect.height);
Print(_label, text_rect, kMiddle, text_color);
// Returns true if the value has changed or if it was clamped in _min / _max
// bounds.
return initial_value != *_value;
}
bool ImGuiImpl::DoSlider2D(const char* _label, ozz::array<float, 2> _min,
ozz::array<float, 2> _max,
ozz::array<float, 2>* _value, bool _enabled) {
math::RectFloat rect;
if (!AddWidget(-1.f, &rect)) {
return false;
}
auto_gen_id_++;
// Calculate mouse cursor's relative y offset.
const ozz::array<float, 2> initial_value = *_value;
// Check for hotness.
bool hot = false, active = false;
if (_enabled) {
// Includes the cursor size in the pick region.
math::RectFloat pick_rect = rect;
pick_rect.left -= kWidgetHeight / 2.f;
pick_rect.width += kWidgetHeight;
pick_rect.bottom -= kWidgetHeight / 2.f;
pick_rect.height += kWidgetHeight;
ButtonLogic(pick_rect, auto_gen_id_, &hot, &active);
// A slider is active on lmb pressed, not released. It's different to the
// usual button behavior.
active &= inputs_.lmb_pressed;
}
// Render the scrollbar
const GLubyte* background_color = kSliderBackgroundColor;
const GLubyte* border_color = kWidgetBorderColor;
const GLubyte* slider_color = kSliderCursorColor;
const GLubyte* slider_border_color = kWidgetBorderColor;
const GLubyte* text_color = kWidgetTextColor;
if (!_enabled) {
background_color = kWidgetDisabledBackgroundColor;
border_color = kWidgetDisabledBorderColor;
slider_color = kSliderDisabledCursorColor;
slider_border_color = kWidgetDisabledBorderColor;
text_color = kWidgetDisabledTextColor;
} else if (hot) {
if (active) {
int mousepos = inputs_.mouse_x - static_cast<int>(rect.left);
if (mousepos < 0) {
mousepos = 0;
}
if (mousepos > rect.width) {
mousepos = static_cast<int>(rect.width);
}
pow_value = (mousepos * (pow_max - pow_min)) / rect.width + pow_min;
*_value = ozz::math::Clamp(_min, powf(pow_value, 1.f / _pow), _max);
// Button is both 'hot' and 'active'.
slider_color = kWidgetActiveBackgroundColor;
slider_border_color = kWidgetActiveBorderColor;
} else {
// Clamping is only applied if the widget is enabled.
*_value = clamped_value;
// Button is merely 'hot'.
slider_color = kSliderCursorHotColor;
slider_border_color = kWidgetHotBorderColor;
}
} else {
// button is not hot, but it may be active. Use default colors.
}
// Update widget value.
ozz::array<float, 2> cursor;
const int mouse[2] = {inputs_.mouse_x - static_cast<int>(rect.left),
inputs_.mouse_y - static_cast<int>(rect.bottom)};
const int mouse_max[2] = {static_cast<int>(rect.width),
static_cast<int>(rect.height)};
for (size_t i = 0; i < 2; ++i) {
_value->at(i) =
SliderControl(initial_value[i], _min[i], _max[i], 1.f, mouse[i],
mouse_max[i], _enabled, active, &cursor[i]);
}
// Renders slider's rail.
@@ -822,14 +929,24 @@ bool ImGuiImpl::DoSlider(const char* _label, float _min, float _max,
StrokeRect(rail_rect, kSliderRoundRectRadius, border_color);
// Finds cursor position and rect.
const float cursor =
floorf((rect.width * (pow_value - pow_min)) / (pow_max - pow_min));
const math::RectFloat cursor_rect(
rect.left + cursor - kWidgetCursorWidth / 2.f, rect.bottom - 1.f,
kWidgetCursorWidth, rect.height + 2.f);
rect.left + cursor[0] - kWidgetHeight / 2.f,
rect.bottom + cursor[1] - kWidgetHeight / 2.f, kWidgetHeight,
kWidgetHeight);
// Cursor cross
const math::RectFloat cross_hrect(rect.left, rect.bottom + cursor[1],
rect.width, 1);
StrokeRect(cross_hrect, 0.f, slider_color);
const math::RectFloat cross_vrect(rect.left + cursor[0], rect.bottom, 1,
rect.height);
StrokeRect(cross_vrect, 0.f, slider_color);
// Draw slider
FillRect(cursor_rect, kSliderRoundRectRadius, slider_color);
StrokeRect(cursor_rect, kSliderRoundRectRadius, slider_border_color);
// Text
const math::RectFloat text_rect(
rail_rect.left + kSliderRoundRectRadius, rail_rect.bottom,
rail_rect.width - kSliderRoundRectRadius * 2.f, rail_rect.height);
@@ -1361,7 +1478,9 @@ float ImGuiImpl::Print(const char* _text, const math::RectFloat& _rect,
ly = _rect.bottom + (line_count - 1) * (font_.glyph_height + interlign);
break;
}
default: { break; }
default: {
break;
}
}
GL(BindTexture(GL_TEXTURE_2D, glyph_texture_));
@@ -1385,7 +1504,9 @@ float ImGuiImpl::Print(const char* _text, const math::RectFloat& _rect,
lx = _rect.right() - (line_char_count * font_.glyph_width);
break;
}
default: { break; }
default: {
break;
}
}
// Loops through all characters of the current line, and renders them using
@@ -36,10 +36,8 @@
// 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 {
@@ -90,6 +88,10 @@ class ImGuiImpl : public ImGui {
virtual bool DoSlider(const char* _label, float _min, float _max,
float* _value, float _pow, bool _enabled);
virtual bool DoSlider2D(const char* _label, ozz::array<float, 2> _min,
ozz::array<float, 2> _max,
ozz::array<float, 2>* _value, bool _enabled);
virtual bool DoSlider(const char* _label, int _min, int _max, int* _value,
float _pow, bool _enabled);
@@ -132,6 +132,12 @@ bool RendererImpl::Initialize() {
}
}
// Instantiate instanced ambient rendering shader.
points_shader = PointsShader::Build();
if (!points_shader) {
return false;
}
return true;
}
@@ -239,7 +245,7 @@ bool RendererImpl::DrawGrid(int _cell_count, float _cell_size) {
return true;
}
// Computes the model space bind pose and renders it.
// Computes the model space rest pose and renders it.
bool RendererImpl::DrawSkeleton(const ozz::animation::Skeleton& _skeleton,
const ozz::math::Float4x4& _transform,
bool _draw_joints) {
@@ -253,9 +259,9 @@ bool RendererImpl::DrawSkeleton(const ozz::animation::Skeleton& _skeleton,
// Reallocate matrix array if necessary.
prealloc_models_.resize(num_joints);
// Compute model space bind pose.
// Compute model space rest pose.
ozz::animation::LocalToModelJob job;
job.input = _skeleton.joint_bind_poses();
job.input = _skeleton.joint_rest_poses();
job.output = make_span(prealloc_models_);
job.skeleton = &_skeleton;
if (!job.Run()) {
@@ -460,10 +466,7 @@ int DrawPosture_FillUniforms(const ozz::animation::Skeleton& _skeleton,
// Copy parent joint's raw matrix, to render a bone between the parent
// and current matrix.
float* uniform = _uniforms + instances * 16;
math::StorePtr(parent.cols[0], uniform + 0);
math::StorePtr(parent.cols[1], uniform + 4);
math::StorePtr(parent.cols[2], uniform + 8);
math::StorePtr(parent.cols[3], uniform + 12);
std::memcpy(uniform, parent.cols, 16 * sizeof(float));
// Set bone direction (bone_dir). The shader expects to find it at index
// [3,7,11] of the matrix.
@@ -483,11 +486,7 @@ int DrawPosture_FillUniforms(const ozz::animation::Skeleton& _skeleton,
// Only the joint is rendered for leaves, the bone model isn't.
if (IsLeaf(_skeleton, i)) {
// Copy current joint's raw matrix.
uniform = _uniforms + instances * 16;
math::StorePtr(current.cols[0], uniform + 0);
math::StorePtr(current.cols[1], uniform + 4);
math::StorePtr(current.cols[2], uniform + 8);
math::StorePtr(current.cols[3], uniform + 12);
std::memcpy(uniform, current.cols, 16 * sizeof(float));
// Re-use bone_dir to fix the size of the leaf (same as previous bone).
// The shader expects to find it at index [3,7,11] of the matrix.
@@ -621,6 +620,84 @@ bool RendererImpl::DrawPosture(const ozz::animation::Skeleton& _skeleton,
return true;
}
bool RendererImpl::DrawPoints(const ozz::span<const float>& _positions,
const ozz::span<const float>& _sizes,
const ozz::span<const Color>& _colors,
const ozz::math::Float4x4& _transform,
bool _round, bool _screen_space) {
// Early out if no instance to render.
if (_positions.size() == 0) {
return true;
}
// Sizes and colors must be of size 1 or equal to _positions' size.
if (_sizes.size() != 1 && _sizes.size() != _positions.size() / 3) {
return false;
}
if (_colors.size() != 1 && _colors.size() != _positions.size() / 3) {
return false;
}
const GLsizei positions_size = static_cast<GLsizei>(_positions.size_bytes());
const GLsizei colors_size =
static_cast<GLsizei>(_colors.size() == 1 ? 0 : _colors.size_bytes());
const GLsizei sizes_size =
static_cast<GLsizei>(_sizes.size() == 1 ? 0 : _sizes.size_bytes());
const GLsizei buffer_size = positions_size + colors_size + sizes_size;
const GLsizei positions_offset = 0;
const GLsizei colors_offset = positions_offset + positions_size;
const GLsizei sizes_offset = colors_offset + colors_size;
// Reallocate vertex buffer.
GL(BindBuffer(GL_ARRAY_BUFFER, dynamic_array_bo_));
GL(BufferData(GL_ARRAY_BUFFER, buffer_size, nullptr, GL_STREAM_DRAW));
GL(BufferSubData(GL_ARRAY_BUFFER, positions_offset, positions_size,
_positions.data()));
GL(BufferSubData(GL_ARRAY_BUFFER, colors_offset, colors_size,
_colors.data()));
GL(BufferSubData(GL_ARRAY_BUFFER, sizes_offset, sizes_size, _sizes.data()));
// Square or round sprites. Beware msaa makes sprites round if GL_POINT_SPRITE
// isn't enabled
if (_round) {
GL(Enable(GL_POINT_SMOOTH));
GL(Disable(GL_POINT_SPRITE));
} else {
GL(Disable(GL_POINT_SMOOTH));
GL(Enable(GL_POINT_SPRITE));
}
// Size is managed in vertex shader side.
GL(Enable(GL_PROGRAM_POINT_SIZE));
const PointsShader::GenericAttrib attrib =
points_shader->Bind(_transform, camera()->view_proj(), 12,
positions_offset, colors_size ? 4 : 0, colors_offset,
sizes_size ? 4 : 0, sizes_offset, _screen_space);
GL(BindBuffer(GL_ARRAY_BUFFER, 0));
// Apply remaining general attributes
if (_colors.size() <= 1) {
const Color color = _colors.empty() ? kWhite : _colors[0];
GL(VertexAttrib4f(attrib.color, color.r / 255.f, color.g / 255.f,
color.b / 255.f, color.a / 255.f));
}
if (_sizes.size() <= 1) {
const float size = _sizes.empty() ? 1.f : _sizes[0];
GL(VertexAttrib1f(attrib.size, size));
}
// Draws the mesh.
GL(DrawArrays(GL_POINTS, 0, static_cast<GLsizei>(_positions.size() / 3)));
// Unbinds.
points_shader->Unbind();
return true;
}
bool RendererImpl::DrawBoxIm(const ozz::math::Box& _box,
const ozz::math::Float4x4& _transform,
const Color _colors[2]) {
@@ -1218,49 +1295,70 @@ bool RendererImpl::DrawMesh(const Mesh& _mesh,
vertex_offset += part_vertex_count;
}
// Binds shader with this array buffer, depending on rendering options.
Shader* shader = nullptr;
if (_options.texture) {
ambient_textured_shader->Bind(_transform, camera()->view_proj(),
positions_stride, positions_offset,
normals_stride, normals_offset, colors_stride,
colors_offset, uvs_stride, uvs_offset);
shader = ambient_textured_shader.get();
if (_options.triangles) {
// Binds shader with this array buffer, depending on rendering options.
Shader* shader = nullptr;
if (_options.texture) {
ambient_textured_shader->Bind(
_transform, camera()->view_proj(), positions_stride, positions_offset,
normals_stride, normals_offset, colors_stride, colors_offset,
uvs_stride, uvs_offset);
shader = ambient_textured_shader.get();
// Binds default texture
GL(BindTexture(GL_TEXTURE_2D, checkered_texture_));
} else {
ambient_shader->Bind(_transform, camera()->view_proj(), positions_stride,
positions_offset, normals_stride, normals_offset,
colors_stride, colors_offset);
shader = ambient_shader.get();
// Binds default texture
GL(BindTexture(GL_TEXTURE_2D, checkered_texture_));
} else {
ambient_shader->Bind(_transform, camera()->view_proj(), positions_stride,
positions_offset, normals_stride, normals_offset,
colors_stride, colors_offset);
shader = ambient_shader.get();
}
// Maps the index dynamic buffer and update it.
GL(BindBuffer(GL_ELEMENT_ARRAY_BUFFER, dynamic_index_bo_));
const Mesh::TriangleIndices& indices = _mesh.triangle_indices;
GL(BufferData(GL_ELEMENT_ARRAY_BUFFER,
indices.size() * sizeof(Mesh::TriangleIndices::value_type),
array_begin(indices), GL_STREAM_DRAW));
// Draws the mesh.
static_assert(sizeof(Mesh::TriangleIndices::value_type) == 2,
"Expects 2 bytes indices.");
GL(DrawElements(GL_TRIANGLES, static_cast<GLsizei>(indices.size()),
GL_UNSIGNED_SHORT, 0));
// Unbinds.
GL(BindBuffer(GL_ARRAY_BUFFER, 0));
GL(BindTexture(GL_TEXTURE_2D, 0));
GL(BindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
shader->Unbind();
}
// Maps the index dynamic buffer and update it.
GL(BindBuffer(GL_ELEMENT_ARRAY_BUFFER, dynamic_index_bo_));
const Mesh::TriangleIndices& indices = _mesh.triangle_indices;
GL(BufferData(GL_ELEMENT_ARRAY_BUFFER,
indices.size() * sizeof(Mesh::TriangleIndices::value_type),
array_begin(indices), GL_STREAM_DRAW));
// Draws the mesh.
static_assert(sizeof(Mesh::TriangleIndices::value_type) == 2,
"Expects 2 bytes indices.");
GL(DrawElements(GL_TRIANGLES, static_cast<GLsizei>(indices.size()),
GL_UNSIGNED_SHORT, 0));
// Unbinds.
GL(BindBuffer(GL_ARRAY_BUFFER, 0));
GL(BindTexture(GL_TEXTURE_2D, 0));
GL(BindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
shader->Unbind();
if (_options.wireframe) {
#ifndef EMSCRIPTEN
GL(PolygonMode(GL_FRONT_AND_BACK, GL_FILL));
#endif // EMSCRIPTEN
}
// Renders debug vertices.
if (_options.vertices) {
for (size_t i = 0; i < _mesh.parts.size(); ++i) {
const Mesh::Part& part = _mesh.parts[i];
ozz::sample::Color color = ozz::sample::kWhite;
span<const ozz::sample::Color> colors;
if (_options.colors && part.colors.size() == part.positions.size() / 3) {
colors = {
reinterpret_cast<const ozz::sample::Color*>(part.colors.data()),
part.positions.size() / 3};
} else {
colors = {&color, 1};
}
const float size = 2.f;
DrawPoints({part.positions.data(), part.positions.size()}, {&size, 1},
{&color, 1}, _transform, true, true);
}
}
// Renders debug normals.
if (_options.normals) {
for (size_t i = 0; i < _mesh.parts.size(); ++i) {
@@ -1313,7 +1411,7 @@ bool RendererImpl::DrawSkinnedMesh(
const Mesh& _mesh, const span<math::Float4x4> _skinning_matrices,
const ozz::math::Float4x4& _transform, const Options& _options) {
// Forward to DrawMesh function is skinning is disabled.
if (_options.skip_skinning) {
if (_options.skip_skinning || !_mesh.skinned()) {
return DrawMesh(_mesh, _transform, _options);
}
@@ -1327,13 +1425,16 @@ bool RendererImpl::DrawSkinnedMesh(
// Positions and normals are interleaved to improve caching while executing
// skinning job.
const GLsizei positions_offset = 0;
const GLsizei normals_offset = sizeof(float) * 3;
const GLsizei tangents_offset = sizeof(float) * 6;
const GLsizei positions_stride = sizeof(float) * 9;
const GLsizei normals_stride = positions_stride;
const GLsizei tangents_stride = positions_stride;
const GLsizei skinned_data_size = vertex_count * positions_stride;
const GLsizei positions_stride = sizeof(float) * 3;
const GLsizei normals_offset = vertex_count * positions_stride;
const GLsizei normals_stride = sizeof(float) * 3;
const GLsizei tangents_offset =
normals_offset + vertex_count * normals_stride;
const GLsizei tangents_stride = sizeof(float) * 3;
const GLsizei skinned_data_size =
tangents_offset + vertex_count * tangents_stride;
// Colors and uvs are contiguous. They aren't transformed, so they can be
// directly copied from source mesh which is non-interleaved as-well.
@@ -1542,54 +1643,76 @@ bool RendererImpl::DrawSkinnedMesh(
processed_vertex_count += part_vertex_count;
}
// Updates dynamic vertex buffer with skinned data.
GL(BindBuffer(GL_ARRAY_BUFFER, dynamic_array_bo_));
GL(BufferData(GL_ARRAY_BUFFER, vbo_size, nullptr, GL_STREAM_DRAW));
GL(BufferSubData(GL_ARRAY_BUFFER, 0, vbo_size, vbo_map));
if (_options.triangles) {
// Updates dynamic vertex buffer with skinned data.
GL(BindBuffer(GL_ARRAY_BUFFER, dynamic_array_bo_));
GL(BufferData(GL_ARRAY_BUFFER, vbo_size, nullptr, GL_STREAM_DRAW));
GL(BufferSubData(GL_ARRAY_BUFFER, 0, vbo_size, vbo_map));
// Binds shader with this array buffer, depending on rendering options.
Shader* shader = nullptr;
if (_options.texture) {
ambient_textured_shader->Bind(_transform, camera()->view_proj(),
positions_stride, positions_offset,
normals_stride, normals_offset, colors_stride,
colors_offset, uvs_stride, uvs_offset);
shader = ambient_textured_shader.get();
// Binds shader with this array buffer, depending on rendering options.
Shader* shader = nullptr;
if (_options.texture) {
ambient_textured_shader->Bind(
_transform, camera()->view_proj(), positions_stride, positions_offset,
normals_stride, normals_offset, colors_stride, colors_offset,
uvs_stride, uvs_offset);
shader = ambient_textured_shader.get();
// Binds default texture
GL(BindTexture(GL_TEXTURE_2D, checkered_texture_));
} else {
ambient_shader->Bind(_transform, camera()->view_proj(), positions_stride,
positions_offset, normals_stride, normals_offset,
colors_stride, colors_offset);
shader = ambient_shader.get();
// Binds default texture
GL(BindTexture(GL_TEXTURE_2D, checkered_texture_));
} else {
ambient_shader->Bind(_transform, camera()->view_proj(), positions_stride,
positions_offset, normals_stride, normals_offset,
colors_stride, colors_offset);
shader = ambient_shader.get();
}
// Maps the index dynamic buffer and update it.
GL(BindBuffer(GL_ELEMENT_ARRAY_BUFFER, dynamic_index_bo_));
const Mesh::TriangleIndices& indices = _mesh.triangle_indices;
GL(BufferData(GL_ELEMENT_ARRAY_BUFFER,
indices.size() * sizeof(Mesh::TriangleIndices::value_type),
array_begin(indices), GL_STREAM_DRAW));
// Draws the mesh.
static_assert(sizeof(Mesh::TriangleIndices::value_type) == 2,
"Expects 2 bytes indices.");
GL(DrawElements(GL_TRIANGLES, static_cast<GLsizei>(indices.size()),
GL_UNSIGNED_SHORT, 0));
// Unbinds.
GL(BindBuffer(GL_ARRAY_BUFFER, 0));
GL(BindTexture(GL_TEXTURE_2D, 0));
GL(BindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
shader->Unbind();
}
// Maps the index dynamic buffer and update it.
GL(BindBuffer(GL_ELEMENT_ARRAY_BUFFER, dynamic_index_bo_));
const Mesh::TriangleIndices& indices = _mesh.triangle_indices;
GL(BufferData(GL_ELEMENT_ARRAY_BUFFER,
indices.size() * sizeof(Mesh::TriangleIndices::value_type),
array_begin(indices), GL_STREAM_DRAW));
// Draws the mesh.
static_assert(sizeof(Mesh::TriangleIndices::value_type) == 2,
"Expects 2 bytes indices.");
GL(DrawElements(GL_TRIANGLES, static_cast<GLsizei>(indices.size()),
GL_UNSIGNED_SHORT, 0));
// Unbinds.
GL(BindBuffer(GL_ARRAY_BUFFER, 0));
GL(BindTexture(GL_TEXTURE_2D, 0));
GL(BindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
shader->Unbind();
if (_options.wireframe) {
#ifndef EMSCRIPTEN
GL(PolygonMode(GL_FRONT_AND_BACK, GL_FILL));
#endif // EMSCRIPTEN
}
// Renders debug vertices.
if (_options.vertices) {
ozz::sample::Color color = ozz::sample::kWhite;
span<const ozz::sample::Color> colors;
if (_options.colors) {
colors = {reinterpret_cast<const ozz::sample::Color*>(
ozz::PointerStride(vbo_map, colors_offset)),
static_cast<size_t>(vertex_count)};
} else {
colors = {&color, 1};
}
const span<const float> vertices{
reinterpret_cast<const float*>(
ozz::PointerStride(vbo_map, positions_offset)),
static_cast<size_t>(vertex_count * 3)};
const float size = 2.f;
DrawPoints(vertices, {&size, 1}, colors, _transform, true, true);
}
return true;
}
@@ -59,7 +59,6 @@
// 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"
@@ -91,6 +90,7 @@ namespace sample {
namespace internal {
class Camera;
class Shader;
class PointsShader;
class SkeletonShader;
class AmbientShader;
class AmbientTexturedShader;
@@ -119,6 +119,12 @@ class RendererImpl : public Renderer {
const ozz::math::Float4x4& _transform,
bool _draw_joints);
virtual bool DrawPoints(
const ozz::span<const float>& _positions,
const ozz::span<const float>& _sizes,
const ozz::span<const Color>& _colors,
const ozz::math::Float4x4& _transform, bool _round, bool _screen_space);
virtual bool DrawBoxIm(const ozz::math::Box& _box,
const ozz::math::Float4x4& _transform,
const Color _colors[2]);
@@ -241,6 +247,7 @@ class RendererImpl : public Renderer {
ozz::unique_ptr<AmbientShader> ambient_shader;
ozz::unique_ptr<AmbientTexturedShader> ambient_textured_shader;
ozz::unique_ptr<AmbientShaderInstanced> ambient_shader_instanced;
ozz::unique_ptr<PointsShader> points_shader;
// Checkered texture
unsigned int checkered_texture_;
@@ -326,6 +326,91 @@ void ImmediatePTCShader::Bind(const math::Float4x4& _model,
GL(Uniform1i(texture, 0));
}
ozz::unique_ptr<PointsShader> PointsShader::Build() {
bool success = true;
const char* kSimplePointsVS =
"uniform mat4 u_mvp;\n"
"attribute vec3 a_position;\n"
"attribute vec4 a_color;\n"
"attribute float a_size;\n"
"attribute float a_screen_space;\n"
"varying vec4 v_vertex_color;\n"
"void main() {\n"
" vec4 vertex = vec4(a_position.xyz, 1.);\n"
" gl_Position = u_mvp * vertex;\n"
" gl_PointSize = a_screen_space == 0. ? a_size / gl_Position.w : a_size;\n"
" v_vertex_color = a_color;\n"
"}\n";
const char* kSimplePointsPS =
"varying vec4 v_vertex_color;\n"
"void main() {\n"
" gl_FragColor = v_vertex_color;\n"
"}\n";
const char* vs[] = {kPlatformSpecivicVSHeader, kSimplePointsVS};
const char* fs[] = {kPlatformSpecivicFSHeader, kSimplePointsPS};
ozz::unique_ptr<PointsShader> shader = make_unique<PointsShader>();
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");
success &= shader->FindAttrib("a_size");
success &= shader->FindAttrib("a_screen_space");
// Binds default uniforms
success &= shader->BindUniform("u_mvp");
if (!success) {
shader.reset();
}
return shader;
}
PointsShader::GenericAttrib PointsShader::Bind(
const math::Float4x4& _model, const math::Float4x4& _view_proj,
GLsizei _pos_stride, GLsizei _pos_offset, GLsizei _color_stride,
GLsizei _color_offset, GLsizei _size_stride, GLsizei _size_offset,
bool _screen_space) {
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);
if (_color_stride) {
GL(EnableVertexAttribArray(color_attrib));
GL(VertexAttribPointer(color_attrib, 4, GL_UNSIGNED_BYTE, GL_TRUE,
_color_stride, GL_PTR_OFFSET(_color_offset)));
}
const GLint size_attrib = attrib(2);
if (_size_stride) {
GL(EnableVertexAttribArray(size_attrib));
GL(VertexAttribPointer(size_attrib, 1, GL_FLOAT, GL_TRUE, _size_stride,
GL_PTR_OFFSET(_size_offset)));
}
const GLint screen_space_attrib = attrib(3);
GL(VertexAttrib1f(screen_space_attrib, 1.f * _screen_space));
// 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));
return {_color_stride ? -1 : color_attrib, _size_stride ? -1 : size_attrib};
}
namespace {
const char* kPassUv =
"attribute vec2 a_uv;\n"
@@ -126,6 +126,29 @@ class ImmediatePTCShader : public Shader {
GLsizei _tex_offset, GLsizei _color_stride, GLsizei _color_offset);
};
class PointsShader : public Shader {
public:
PointsShader() {}
virtual ~PointsShader() {}
// 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<PointsShader> Build();
// Binds the shader.
struct GenericAttrib {
GLint color;
GLint size;
};
GenericAttrib Bind(const math::Float4x4& _model,
const math::Float4x4& _view_proj, GLsizei _pos_stride,
GLsizei _pos_offset, GLsizei _color_stride,
GLsizei _color_offset, GLsizei _size_stride,
GLsizei _size_offset, bool _screen_space);
};
class SkeletonShader : public Shader {
public:
SkeletonShader() {}
+1 -6
View File
@@ -69,12 +69,7 @@ struct Mesh {
// 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;
return !inverse_bind_poses.empty();
}
// Returns the number of joints used to skin the mesh.
+24 -6
View File
@@ -79,7 +79,7 @@ class Renderer {
// has a size of _cell_size.
virtual bool DrawGrid(int _cell_count, float _cell_size) = 0;
// Renders a skeleton in its bind pose posture.
// Renders a skeleton in its rest pose posture.
virtual bool DrawSkeleton(const animation::Skeleton& _skeleton,
const ozz::math::Float4x4& _transform,
bool _draw_joints = true) = 0;
@@ -93,6 +93,17 @@ class Renderer {
const ozz::math::Float4x4& _transform,
bool _draw_joints = true) = 0;
// Renders points.
// _sizes and _colors must be either of ize 1 or equal to _positions' size.
// If _screen_space is true, then points size is fixed in screen-space,
// otherwise it changes with screen depth.
virtual bool DrawPoints(
const ozz::span<const float>& _positions,
const ozz::span<const float>& _sizes,
const ozz::span<const Color>& _colors,
const ozz::math::Float4x4& _transform, bool _round = true,
bool _screen_space = false) = 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.
@@ -116,16 +127,20 @@ class Renderer {
Color _color) = 0;
struct Options {
bool triangles; // Show triangles mesh.
bool texture; // Show texture (default checkered texture).
bool vertices; // Show vertices as points.
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 wireframe; // Show vertex colors.
bool skip_skinning; // Show texture (default checkered texture).
Options()
: texture(false),
: triangles(true),
texture(false),
vertices(false),
normals(false),
tangents(false),
binormals(false),
@@ -133,9 +148,12 @@ class Renderer {
wireframe(false),
skip_skinning(false) {}
Options(bool _texture, bool _normals, bool _tangents, bool _binormals,
bool _colors, bool _wireframe, bool _skip_skinning)
: texture(_texture),
Options(bool _triangles, bool _texture, bool _vertices, bool _normals,
bool _tangents, bool _binormals, bool _colors, bool _wireframe,
bool _skip_skinning)
: triangles(_triangles),
texture(_texture),
vertices(_vertices),
normals(_normals),
tangents(_tangents),
binormals(_binormals),
@@ -1,7 +1,7 @@
# Adds fbx2mesh utility target.
if(ozz_build_fbx)
# share meshes with thte sample framework
# share meshes with the sample framework
add_executable(sample_fbx2mesh
fbx2mesh.cc
${PROJECT_SOURCE_DIR}/samples/framework/mesh.cc
@@ -9,6 +9,7 @@ if(ozz_build_fbx)
target_link_libraries(sample_fbx2mesh
ozz_animation_fbx
ozz_options)
target_copy_shared_libraries(sample_fbx2mesh)
set_target_properties(sample_fbx2mesh
PROPERTIES FOLDER "samples/tools")
+19 -20
View File
@@ -25,12 +25,12 @@
// //
//----------------------------------------------------------------------------//
#include <algorithm>
#include <limits>
#include "framework/mesh.h"
#include "ozz/animation/offline/fbx/fbx.h"
#include "ozz/animation/runtime/skeleton.h"
#include "ozz/base/containers/map.h"
#include "ozz/base/containers/vector.h"
#include "ozz/base/io/archive.h"
@@ -39,14 +39,8 @@
#include "ozz/base/maths/math_ex.h"
#include "ozz/base/maths/simd_math.h"
#include "ozz/base/memory/allocator.h"
#include "ozz/options/options.h"
#include <algorithm>
#include <limits>
#include "fbxsdk/utils/fbxgeometryconverter.h"
// Declares command line options.
OZZ_OPTIONS_DECLARE_STRING(file, "Specifies input file.", "", true)
OZZ_OPTIONS_DECLARE_STRING(skeleton,
@@ -504,19 +498,18 @@ bool BuildSkin(FbxMesh* _fbx_mesh,
const int* ctrl_point_indices = cluster->GetControlPointIndices();
const double* ctrl_point_weights = cluster->GetControlPointWeights();
for (int cpi = 0; cpi < ctrl_point_index_count; ++cpi) {
const SkinMapping mapping = {joint,
static_cast<float>(ctrl_point_weights[cpi])};
if (mapping.weight <= 0.f) {
continue;
}
const int ctrl_point = ctrl_point_indices[cpi];
assert(ctrl_point < static_cast<int>(_remap.size()));
// It happens that weight is 0. In this case give the vertex a very small
// weight so normalization succeeds.
const float ctrl_point_weight =
static_cast<float>(ctrl_point_weights[cpi]);
const SkinMapping mapping = {
joint, ctrl_point_weight == 0.f ? 1e-9f : ctrl_point_weight};
// remap.size() can be 0, skinned control point might not be used by any
// polygon of the mesh. Sometimes, the mesh can have less points than at
// the time of the skinning because a smooth operator was active when
// skinning but has been deactivated during export.
const int ctrl_point = ctrl_point_indices[cpi];
const ControlPointRemap& remap = _remap[ctrl_point];
for (size_t v = 0; v < remap.size(); ++v) {
vertex_skin_mappings[remap[v]].push_back(mapping);
@@ -560,6 +553,11 @@ bool BuildSkin(FbxMesh* _fbx_mesh,
// Stores joint's indices and weights.
size_t influence_count = inv.size();
if (influence_count == 0) {
vertex_skin_mappings[i].push_back({0,1.f});
influence_count = 1;
}
if (influence_count > 0) {
size_t j = 0;
for (; j < influence_count; ++j) {
@@ -579,11 +577,12 @@ bool BuildSkin(FbxMesh* _fbx_mesh,
}
if (vertex_isnt_influenced) {
ozz::log::Err() << "At least one vertex isn't influenced by any joints."
<< std::endl;
ozz::log::LogV() << "At least one vertex isn't influenced by any joints. "
"It's been reassigned to root joint."
<< std::endl;
}
return !vertex_isnt_influenced;
return true;
}
// Limits the number of joints influencing a vertex.
+11 -17
View File
@@ -30,28 +30,22 @@
#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 "framework/imgui.h"
#include "framework/mesh.h"
#include "ozz/animation/offline/raw_skeleton.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"
#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/geometry/runtime/skinning_job.h"
namespace ozz {
namespace sample {
@@ -224,9 +218,9 @@ void ComputeSkeletonBounds(const animation::Skeleton& _skeleton,
// Allocate matrix array, out of memory is handled by the LocalToModelJob.
ozz::vector<ozz::math::Float4x4> models(num_joints);
// Compute model space bind pose.
// Compute model space rest pose.
ozz::animation::LocalToModelJob job;
job.input = _skeleton.joint_bind_poses();
job.input = _skeleton.joint_rest_poses();
job.output = make_span(models);
job.skeleton = &_skeleton;
if (job.Run()) {