Light animation support (#509)
This commit is contained in:
Binary file not shown.
Binary file not shown.
+149
-26
@@ -81,8 +81,7 @@ void AnimationWindow::Create(EditorComponent* _editor)
|
||||
loopedCheckBox.SetCheckText(ICON_LOOP);
|
||||
AddWidget(&loopedCheckBox);
|
||||
|
||||
playButton.Create("Play");
|
||||
playButton.SetTooltip("Play/Pause animation.");
|
||||
playButton.Create(ICON_PLAY);
|
||||
playButton.SetSize(XMFLOAT2(100, hei));
|
||||
playButton.SetPos(XMFLOAT2(loopedCheckBox.GetPos().x + loopedCheckBox.GetSize().x + 5, y));
|
||||
playButton.OnClick([&](wi::gui::EventArgs args) {
|
||||
@@ -101,9 +100,9 @@ void AnimationWindow::Create(EditorComponent* _editor)
|
||||
});
|
||||
AddWidget(&playButton);
|
||||
|
||||
stopButton.Create("Stop");
|
||||
stopButton.SetTooltip("Stop animation.");
|
||||
stopButton.SetSize(XMFLOAT2(100, hei));
|
||||
stopButton.Create(ICON_STOP);
|
||||
stopButton.SetTooltip("Stop");
|
||||
stopButton.SetSize(XMFLOAT2(70, hei));
|
||||
stopButton.SetPos(XMFLOAT2(playButton.GetPos().x + playButton.GetSize().x + 5, y));
|
||||
stopButton.OnClick([&](wi::gui::EventArgs args) {
|
||||
AnimationComponent* animation = editor->GetCurrentScene().animations.GetComponent(entity);
|
||||
@@ -197,7 +196,12 @@ void AnimationWindow::Create(EditorComponent* _editor)
|
||||
recordCombo.AddItem("Rotation " ICON_ROTATE);
|
||||
recordCombo.AddItem("Scale " ICON_SCALE);
|
||||
recordCombo.AddItem("Morph weights " ICON_MESH);
|
||||
recordCombo.AddItem("Close loop " ICON_LOOP);
|
||||
recordCombo.AddItem("Light [color] " ICON_POINTLIGHT);
|
||||
recordCombo.AddItem("Light [intensity] " ICON_POINTLIGHT);
|
||||
recordCombo.AddItem("Light [range] " ICON_POINTLIGHT);
|
||||
recordCombo.AddItem("Light [inner cone] " ICON_POINTLIGHT);
|
||||
recordCombo.AddItem("Light [outer cone] " ICON_POINTLIGHT);
|
||||
recordCombo.AddItem("Close loop " ICON_LOOP, ~0ull);
|
||||
recordCombo.OnSelect([&](wi::gui::EventArgs args) {
|
||||
if (args.iValue == 0)
|
||||
return;
|
||||
@@ -209,7 +213,7 @@ void AnimationWindow::Create(EditorComponent* _editor)
|
||||
{
|
||||
const float current_time = animation->timer;
|
||||
|
||||
if (args.iValue == 6)
|
||||
if (args.userdata == ~0ull)
|
||||
{
|
||||
// Close loop:
|
||||
for (auto& channel : animation->channels)
|
||||
@@ -237,6 +241,8 @@ void AnimationWindow::Create(EditorComponent* _editor)
|
||||
switch (channel.path)
|
||||
{
|
||||
case wi::scene::AnimationComponent::AnimationChannel::TRANSLATION:
|
||||
case wi::scene::AnimationComponent::AnimationChannel::SCALE:
|
||||
case wi::scene::AnimationComponent::AnimationChannel::LIGHT_COLOR:
|
||||
{
|
||||
animation_data->keyframe_data.push_back(animation_data->keyframe_data[keyFirst * 3 + 0]);
|
||||
animation_data->keyframe_data.push_back(animation_data->keyframe_data[keyFirst * 3 + 1]);
|
||||
@@ -251,13 +257,6 @@ void AnimationWindow::Create(EditorComponent* _editor)
|
||||
animation_data->keyframe_data.push_back(animation_data->keyframe_data[keyFirst * 4 + 3]);
|
||||
}
|
||||
break;
|
||||
case wi::scene::AnimationComponent::AnimationChannel::SCALE:
|
||||
{
|
||||
animation_data->keyframe_data.push_back(animation_data->keyframe_data[keyFirst * 3 + 0]);
|
||||
animation_data->keyframe_data.push_back(animation_data->keyframe_data[keyFirst * 3 + 1]);
|
||||
animation_data->keyframe_data.push_back(animation_data->keyframe_data[keyFirst * 3 + 2]);
|
||||
}
|
||||
break;
|
||||
case wi::scene::AnimationComponent::AnimationChannel::WEIGHTS:
|
||||
{
|
||||
const MeshComponent* mesh = scene.meshes.GetComponent(channel.target);
|
||||
@@ -278,6 +277,14 @@ void AnimationWindow::Create(EditorComponent* _editor)
|
||||
}
|
||||
}
|
||||
break;
|
||||
case wi::scene::AnimationComponent::AnimationChannel::LIGHT_INTENSITY:
|
||||
case wi::scene::AnimationComponent::AnimationChannel::LIGHT_RANGE:
|
||||
case wi::scene::AnimationComponent::AnimationChannel::LIGHT_INNERCONE:
|
||||
case wi::scene::AnimationComponent::AnimationChannel::LIGHT_OUTERCONE:
|
||||
{
|
||||
animation_data->keyframe_data.push_back(animation_data->keyframe_data[keyFirst]);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -287,7 +294,7 @@ void AnimationWindow::Create(EditorComponent* _editor)
|
||||
else
|
||||
{
|
||||
// Add keyframe type:
|
||||
wi::vector<AnimationComponent::AnimationChannel::Path> paths;
|
||||
wi::vector<AnimationComponent::AnimationChannel::Path> paths; // stack allocation would be better here..
|
||||
|
||||
switch (args.iValue)
|
||||
{
|
||||
@@ -309,6 +316,21 @@ void AnimationWindow::Create(EditorComponent* _editor)
|
||||
case 5:
|
||||
paths.push_back(AnimationComponent::AnimationChannel::Path::WEIGHTS);
|
||||
break;
|
||||
case 6:
|
||||
paths.push_back(AnimationComponent::AnimationChannel::Path::LIGHT_COLOR);
|
||||
break;
|
||||
case 7:
|
||||
paths.push_back(AnimationComponent::AnimationChannel::Path::LIGHT_INTENSITY);
|
||||
break;
|
||||
case 8:
|
||||
paths.push_back(AnimationComponent::AnimationChannel::Path::LIGHT_RANGE);
|
||||
break;
|
||||
case 9:
|
||||
paths.push_back(AnimationComponent::AnimationChannel::Path::LIGHT_INNERCONE);
|
||||
break;
|
||||
case 10:
|
||||
paths.push_back(AnimationComponent::AnimationChannel::Path::LIGHT_OUTERCONE);
|
||||
break;
|
||||
}
|
||||
|
||||
for (auto path : paths)
|
||||
@@ -421,6 +443,78 @@ void AnimationWindow::Create(EditorComponent* _editor)
|
||||
}
|
||||
}
|
||||
break;
|
||||
case wi::scene::AnimationComponent::AnimationChannel::LIGHT_COLOR:
|
||||
{
|
||||
const LightComponent* light = scene.lights.GetComponent(channel.target);
|
||||
if (light != nullptr)
|
||||
{
|
||||
animation_data->keyframe_data.push_back(light->color.x);
|
||||
animation_data->keyframe_data.push_back(light->color.y);
|
||||
animation_data->keyframe_data.push_back(light->color.z);
|
||||
}
|
||||
else
|
||||
{
|
||||
animation_data->keyframe_times.pop_back();
|
||||
animation->channels.pop_back();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case wi::scene::AnimationComponent::AnimationChannel::LIGHT_INTENSITY:
|
||||
{
|
||||
const LightComponent* light = scene.lights.GetComponent(channel.target);
|
||||
if (light != nullptr)
|
||||
{
|
||||
animation_data->keyframe_data.push_back(light->intensity);
|
||||
}
|
||||
else
|
||||
{
|
||||
animation_data->keyframe_times.pop_back();
|
||||
animation->channels.pop_back();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case wi::scene::AnimationComponent::AnimationChannel::LIGHT_RANGE:
|
||||
{
|
||||
const LightComponent* light = scene.lights.GetComponent(channel.target);
|
||||
if (light != nullptr)
|
||||
{
|
||||
animation_data->keyframe_data.push_back(light->range);
|
||||
}
|
||||
else
|
||||
{
|
||||
animation_data->keyframe_times.pop_back();
|
||||
animation->channels.pop_back();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case wi::scene::AnimationComponent::AnimationChannel::LIGHT_INNERCONE:
|
||||
{
|
||||
const LightComponent* light = scene.lights.GetComponent(channel.target);
|
||||
if (light != nullptr)
|
||||
{
|
||||
animation_data->keyframe_data.push_back(light->innerConeAngle);
|
||||
}
|
||||
else
|
||||
{
|
||||
animation_data->keyframe_times.pop_back();
|
||||
animation->channels.pop_back();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case wi::scene::AnimationComponent::AnimationChannel::LIGHT_OUTERCONE:
|
||||
{
|
||||
const LightComponent* light = scene.lights.GetComponent(channel.target);
|
||||
if (light != nullptr)
|
||||
{
|
||||
animation_data->keyframe_data.push_back(light->outerConeAngle);
|
||||
}
|
||||
else
|
||||
{
|
||||
animation_data->keyframe_times.pop_back();
|
||||
animation->channels.pop_back();
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -438,7 +532,7 @@ void AnimationWindow::Create(EditorComponent* _editor)
|
||||
|
||||
keyframesList.Create("Keyframes");
|
||||
keyframesList.SetSize(XMFLOAT2(wid, 200));
|
||||
keyframesList.SetPos(XMFLOAT2(x, y += step));
|
||||
keyframesList.SetPos(XMFLOAT2(4, y += step));
|
||||
keyframesList.OnSelect([=](wi::gui::EventArgs args) {
|
||||
wi::scene::Scene& scene = editor->GetCurrentScene();
|
||||
AnimationComponent* animation = scene.animations.GetComponent(entity);
|
||||
@@ -453,9 +547,7 @@ void AnimationWindow::Create(EditorComponent* _editor)
|
||||
const AnimationDataComponent* animation_data = scene.animation_datas.GetComponent(sam.data);
|
||||
if (animation_data != nullptr && animation_data->keyframe_times.size() > timeIndex)
|
||||
{
|
||||
wi::vector<float> tmp = animation_data->keyframe_times;
|
||||
std::sort(tmp.begin(), tmp.end());
|
||||
float time = tmp[timeIndex];
|
||||
float time = animation_data->keyframe_times[timeIndex];
|
||||
animation->timer = time;
|
||||
}
|
||||
}
|
||||
@@ -479,6 +571,8 @@ void AnimationWindow::Create(EditorComponent* _editor)
|
||||
switch (channel.path)
|
||||
{
|
||||
case AnimationComponent::AnimationChannel::Path::TRANSLATION:
|
||||
case AnimationComponent::AnimationChannel::Path::SCALE:
|
||||
case AnimationComponent::AnimationChannel::Path::LIGHT_COLOR:
|
||||
animation_data->keyframe_times.erase(animation_data->keyframe_times.begin() + timeIndex);
|
||||
animation_data->keyframe_data.erase(animation_data->keyframe_data.begin() + timeIndex * 3, animation_data->keyframe_data.begin() + timeIndex * 3 + 3);
|
||||
break;
|
||||
@@ -486,10 +580,6 @@ void AnimationWindow::Create(EditorComponent* _editor)
|
||||
animation_data->keyframe_times.erase(animation_data->keyframe_times.begin() + timeIndex);
|
||||
animation_data->keyframe_data.erase(animation_data->keyframe_data.begin() + timeIndex * 4, animation_data->keyframe_data.begin() + timeIndex * 4 + 4);
|
||||
break;
|
||||
case AnimationComponent::AnimationChannel::Path::SCALE:
|
||||
animation_data->keyframe_times.erase(animation_data->keyframe_times.begin() + timeIndex);
|
||||
animation_data->keyframe_data.erase(animation_data->keyframe_data.begin() + timeIndex * 3, animation_data->keyframe_data.begin() + timeIndex * 3 + 3);
|
||||
break;
|
||||
case AnimationComponent::AnimationChannel::Path::WEIGHTS:
|
||||
{
|
||||
MeshComponent* mesh = scene.meshes.GetComponent(channel.target);
|
||||
@@ -506,6 +596,12 @@ void AnimationWindow::Create(EditorComponent* _editor)
|
||||
}
|
||||
}
|
||||
break;
|
||||
case AnimationComponent::AnimationChannel::Path::LIGHT_INTENSITY:
|
||||
case AnimationComponent::AnimationChannel::Path::LIGHT_RANGE:
|
||||
case AnimationComponent::AnimationChannel::Path::LIGHT_INNERCONE:
|
||||
case AnimationComponent::AnimationChannel::Path::LIGHT_OUTERCONE:
|
||||
animation_data->keyframe_times.erase(animation_data->keyframe_times.begin() + timeIndex);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -561,11 +657,13 @@ void AnimationWindow::Update()
|
||||
|
||||
if (animation.IsPlaying())
|
||||
{
|
||||
playButton.SetText("Pause");
|
||||
playButton.SetText(ICON_PAUSE);
|
||||
playButton.SetTooltip("Pause");
|
||||
}
|
||||
else
|
||||
{
|
||||
playButton.SetText("Play");
|
||||
playButton.SetText(ICON_PLAY);
|
||||
playButton.SetTooltip("Play");
|
||||
}
|
||||
|
||||
if(!animation.samplers.empty())
|
||||
@@ -607,7 +705,6 @@ void AnimationWindow::RefreshKeyframesList()
|
||||
wi::gui::TreeList::Item item;
|
||||
switch (channel.path)
|
||||
{
|
||||
default:
|
||||
case wi::scene::AnimationComponent::AnimationChannel::TRANSLATION:
|
||||
item.name += ICON_TRANSLATE " ";
|
||||
break;
|
||||
@@ -620,6 +717,23 @@ void AnimationWindow::RefreshKeyframesList()
|
||||
case wi::scene::AnimationComponent::AnimationChannel::WEIGHTS:
|
||||
item.name += ICON_MESH " ";
|
||||
break;
|
||||
case wi::scene::AnimationComponent::AnimationChannel::LIGHT_COLOR:
|
||||
item.name += ICON_POINTLIGHT " [color] ";
|
||||
break;
|
||||
case wi::scene::AnimationComponent::AnimationChannel::LIGHT_INTENSITY:
|
||||
item.name += ICON_POINTLIGHT " [intensity] ";
|
||||
break;
|
||||
case wi::scene::AnimationComponent::AnimationChannel::LIGHT_RANGE:
|
||||
item.name += ICON_POINTLIGHT " [range] ";
|
||||
break;
|
||||
case wi::scene::AnimationComponent::AnimationChannel::LIGHT_INNERCONE:
|
||||
item.name += ICON_POINTLIGHT " [inner cone] ";
|
||||
break;
|
||||
case wi::scene::AnimationComponent::AnimationChannel::LIGHT_OUTERCONE:
|
||||
item.name += ICON_POINTLIGHT " [outer cone] ";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
const NameComponent* name = scene.names.GetComponent(channel.target);
|
||||
if (name == nullptr)
|
||||
@@ -660,3 +774,12 @@ void AnimationWindow::RefreshKeyframesList()
|
||||
channelIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AnimationWindow::ResizeLayout()
|
||||
{
|
||||
wi::gui::Window::ResizeLayout();
|
||||
const float padding = 4;
|
||||
const float width = GetWidgetAreaSize().x - padding * 2;
|
||||
keyframesList.SetSize(XMFLOAT2(width, keyframesList.GetSize().y));
|
||||
}
|
||||
|
||||
@@ -28,5 +28,7 @@ public:
|
||||
void Update();
|
||||
|
||||
void RefreshKeyframesList();
|
||||
|
||||
void ResizeLayout() override;
|
||||
};
|
||||
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ using namespace wi::scene;
|
||||
void IKWindow::Create(EditorComponent* _editor)
|
||||
{
|
||||
editor = _editor;
|
||||
wi::gui::Window::Create("Inverse Kinematics", wi::gui::Window::WindowControls::COLLAPSE | wi::gui::Window::WindowControls::CLOSE);
|
||||
wi::gui::Window::Create(ICON_IK " Inverse Kinematics", wi::gui::Window::WindowControls::COLLAPSE | wi::gui::Window::WindowControls::CLOSE);
|
||||
SetSize(XMFLOAT2(400, 110));
|
||||
|
||||
closeButton.SetTooltip("Delete InverseKinematicsComponent");
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
#define ICON_MATERIAL ICON_FA_FILL_DRIP
|
||||
#define ICON_WEATHER ICON_FA_CLOUD
|
||||
#define ICON_BONE ICON_FA_BONE
|
||||
#define ICON_IK ICON_FA_HAND_FIST
|
||||
#define ICON_NAME ICON_FA_COMMENT_DOTS
|
||||
|
||||
#define ICON_TERRAIN ICON_FA_MOUNTAIN_SUN
|
||||
|
||||
@@ -46,3 +48,6 @@
|
||||
#define ICON_SQUARE ICON_FA_SQUARE_FULL
|
||||
#define ICON_CUBE ICON_FA_CUBE
|
||||
#define ICON_LOOP ICON_FA_REPEAT
|
||||
#define ICON_PLAY ICON_FA_PLAY
|
||||
#define ICON_PAUSE ICON_FA_PAUSE
|
||||
#define ICON_STOP ICON_FA_STOP
|
||||
|
||||
@@ -9,7 +9,7 @@ using namespace wi::scene;
|
||||
void NameWindow::Create(EditorComponent* _editor)
|
||||
{
|
||||
editor = _editor;
|
||||
wi::gui::Window::Create("Name", wi::gui::Window::WindowControls::COLLAPSE | wi::gui::Window::WindowControls::CLOSE);
|
||||
wi::gui::Window::Create(ICON_NAME " Name", wi::gui::Window::WindowControls::COLLAPSE | wi::gui::Window::WindowControls::CLOSE);
|
||||
SetSize(XMFLOAT2(360, 60));
|
||||
|
||||
closeButton.SetTooltip("Delete NameComponent");
|
||||
|
||||
@@ -272,6 +272,7 @@ void OptionsWindow::Create(EditorComponent* _editor)
|
||||
filterCombo.AddItem("Force " ICON_FORCE, (uint64_t)Filter::Force);
|
||||
filterCombo.AddItem("Emitter " ICON_EMITTER, (uint64_t)Filter::Emitter);
|
||||
filterCombo.AddItem("Hairparticle " ICON_HAIR, (uint64_t)Filter::Hairparticle);
|
||||
filterCombo.AddItem("Inverse Kinematics " ICON_IK, (uint64_t)Filter::IK);
|
||||
filterCombo.SetTooltip("Apply filtering to the Entities");
|
||||
filterCombo.OnSelect([&](wi::gui::EventArgs args) {
|
||||
filter = (Filter)args.userdata;
|
||||
@@ -642,6 +643,8 @@ void OptionsWindow::Create(EditorComponent* _editor)
|
||||
editor->saveButton.sprites[i].params.enableCornerRounding();
|
||||
editor->saveButton.sprites[i].params.corners_rounding[2].radius = 10;
|
||||
}
|
||||
editor->componentsWnd.weatherWnd.default_sky_horizon = dark_point;
|
||||
editor->componentsWnd.weatherWnd.default_sky_zenith = theme_color_idle;
|
||||
|
||||
});
|
||||
AddWidget(&themeCombo);
|
||||
@@ -866,11 +869,16 @@ void OptionsWindow::PushToEntityTree(wi::ecs::Entity entity, int level)
|
||||
{
|
||||
item.name += ICON_WEATHER " ";
|
||||
}
|
||||
if (scene.inverse_kinematics.Contains(entity))
|
||||
{
|
||||
item.name += ICON_IK " ";
|
||||
}
|
||||
if (entity == terragen.terrainEntity)
|
||||
{
|
||||
item.name += ICON_TERRAIN " ";
|
||||
}
|
||||
for (size_t i = 0; i < scene.armatures.GetCount(); ++i)
|
||||
bool bone_found = false;
|
||||
for (size_t i = 0; i < scene.armatures.GetCount() && !bone_found; ++i)
|
||||
{
|
||||
const ArmatureComponent& armature = scene.armatures[i];
|
||||
for (Entity bone : armature.boneCollection)
|
||||
@@ -878,6 +886,7 @@ void OptionsWindow::PushToEntityTree(wi::ecs::Entity entity, int level)
|
||||
if (entity == bone)
|
||||
{
|
||||
item.name += ICON_BONE " ";
|
||||
bone_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1080,7 +1089,7 @@ void OptionsWindow::RefreshEntityTree()
|
||||
}
|
||||
}
|
||||
|
||||
if (has_flag(filter, Filter::All))
|
||||
if (has_flag(filter, Filter::IK))
|
||||
{
|
||||
for (size_t i = 0; i < scene.inverse_kinematics.GetCount(); ++i)
|
||||
{
|
||||
|
||||
@@ -55,6 +55,7 @@ public:
|
||||
Force = 1 << 10,
|
||||
Emitter = 1 << 11,
|
||||
Hairparticle = 1 << 12,
|
||||
IK = 1 << 13,
|
||||
|
||||
All = ~0ull,
|
||||
} filter = Filter::All;
|
||||
|
||||
@@ -772,8 +772,8 @@ void WeatherWindow::Update()
|
||||
{
|
||||
scene.weather = {};
|
||||
scene.weather.SetSimpleSky(true);
|
||||
scene.weather.zenith = XMFLOAT3(1, 1, 1);
|
||||
scene.weather.horizon = wi::Color(20, 20, 20);
|
||||
scene.weather.zenith = default_sky_zenith;
|
||||
scene.weather.horizon = default_sky_horizon;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,9 @@ public:
|
||||
wi::scene::WeatherComponent& GetWeather() const;
|
||||
void InvalidateProbes() const;
|
||||
|
||||
XMFLOAT3 default_sky_horizon = XMFLOAT3(0, 0, 0);
|
||||
XMFLOAT3 default_sky_zenith = XMFLOAT3(0, 0, 0);
|
||||
|
||||
wi::gui::Button primaryButton;
|
||||
wi::gui::CheckBox heightFogCheckBox;
|
||||
wi::gui::Slider fogStartSlider;
|
||||
|
||||
+208
-35
@@ -2791,9 +2791,11 @@ namespace wi::scene
|
||||
const float right = animationdata->keyframe_times[keyRight];
|
||||
|
||||
TransformComponent transform;
|
||||
LightComponent light;
|
||||
|
||||
TransformComponent* target_transform = nullptr;
|
||||
MeshComponent* target_mesh = nullptr;
|
||||
LightComponent* target_light = nullptr;
|
||||
|
||||
if (channel.path == AnimationComponent::AnimationChannel::Path::WEIGHTS)
|
||||
{
|
||||
@@ -2805,6 +2807,19 @@ namespace wi::scene
|
||||
continue;
|
||||
animation.morph_weights_temp.resize(target_mesh->targets.size());
|
||||
}
|
||||
else if (
|
||||
channel.path == AnimationComponent::AnimationChannel::Path::LIGHT_COLOR ||
|
||||
channel.path == AnimationComponent::AnimationChannel::Path::LIGHT_INTENSITY ||
|
||||
channel.path == AnimationComponent::AnimationChannel::Path::LIGHT_RANGE ||
|
||||
channel.path == AnimationComponent::AnimationChannel::Path::LIGHT_INNERCONE ||
|
||||
channel.path == AnimationComponent::AnimationChannel::Path::LIGHT_OUTERCONE
|
||||
)
|
||||
{
|
||||
target_light = lights.GetComponent(channel.target);
|
||||
if (target_light == nullptr)
|
||||
continue;
|
||||
light = *target_light;
|
||||
}
|
||||
else
|
||||
{
|
||||
target_transform = transforms.GetComponent(channel.target);
|
||||
@@ -2850,6 +2865,36 @@ namespace wi::scene
|
||||
}
|
||||
}
|
||||
break;
|
||||
case AnimationComponent::AnimationChannel::Path::LIGHT_COLOR:
|
||||
{
|
||||
assert(animationdata->keyframe_data.size() == animationdata->keyframe_times.size() * 3);
|
||||
light.color = ((const XMFLOAT3*)animationdata->keyframe_data.data())[key];
|
||||
}
|
||||
break;
|
||||
case AnimationComponent::AnimationChannel::Path::LIGHT_INTENSITY:
|
||||
{
|
||||
assert(animationdata->keyframe_data.size() == animationdata->keyframe_times.size());
|
||||
light.intensity = animationdata->keyframe_data[key];
|
||||
}
|
||||
break;
|
||||
case AnimationComponent::AnimationChannel::Path::LIGHT_RANGE:
|
||||
{
|
||||
assert(animationdata->keyframe_data.size() == animationdata->keyframe_times.size());
|
||||
light.range = animationdata->keyframe_data[key];
|
||||
}
|
||||
break;
|
||||
case AnimationComponent::AnimationChannel::Path::LIGHT_INNERCONE:
|
||||
{
|
||||
assert(animationdata->keyframe_data.size() == animationdata->keyframe_times.size());
|
||||
light.innerConeAngle = animationdata->keyframe_data[key];
|
||||
}
|
||||
break;
|
||||
case AnimationComponent::AnimationChannel::Path::LIGHT_OUTERCONE:
|
||||
{
|
||||
assert(animationdata->keyframe_data.size() == animationdata->keyframe_times.size());
|
||||
light.outerConeAngle = animationdata->keyframe_data[key];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -2912,6 +2957,52 @@ namespace wi::scene
|
||||
}
|
||||
}
|
||||
break;
|
||||
case AnimationComponent::AnimationChannel::Path::LIGHT_COLOR:
|
||||
{
|
||||
assert(animationdata->keyframe_data.size() == animationdata->keyframe_times.size() * 3);
|
||||
const XMFLOAT3* data = (const XMFLOAT3*)animationdata->keyframe_data.data();
|
||||
XMVECTOR vLeft = XMLoadFloat3(&data[keyLeft]);
|
||||
XMVECTOR vRight = XMLoadFloat3(&data[keyRight]);
|
||||
XMVECTOR vAnim = XMVectorLerp(vLeft, vRight, t);
|
||||
XMStoreFloat3(&light.color, vAnim);
|
||||
}
|
||||
break;
|
||||
case AnimationComponent::AnimationChannel::Path::LIGHT_INTENSITY:
|
||||
{
|
||||
assert(animationdata->keyframe_data.size() == animationdata->keyframe_times.size());
|
||||
float vLeft = animationdata->keyframe_data[keyLeft];
|
||||
float vRight = animationdata->keyframe_data[keyRight];
|
||||
float vAnim = wi::math::Lerp(vLeft, vRight, t);
|
||||
light.intensity = vAnim;
|
||||
}
|
||||
break;
|
||||
case AnimationComponent::AnimationChannel::Path::LIGHT_RANGE:
|
||||
{
|
||||
assert(animationdata->keyframe_data.size() == animationdata->keyframe_times.size());
|
||||
float vLeft = animationdata->keyframe_data[keyLeft];
|
||||
float vRight = animationdata->keyframe_data[keyRight];
|
||||
float vAnim = wi::math::Lerp(vLeft, vRight, t);
|
||||
light.range = vAnim;
|
||||
}
|
||||
break;
|
||||
case AnimationComponent::AnimationChannel::Path::LIGHT_INNERCONE:
|
||||
{
|
||||
assert(animationdata->keyframe_data.size() == animationdata->keyframe_times.size());
|
||||
float vLeft = animationdata->keyframe_data[keyLeft];
|
||||
float vRight = animationdata->keyframe_data[keyRight];
|
||||
float vAnim = wi::math::Lerp(vLeft, vRight, t);
|
||||
light.innerConeAngle = vAnim;
|
||||
}
|
||||
break;
|
||||
case AnimationComponent::AnimationChannel::Path::LIGHT_OUTERCONE:
|
||||
{
|
||||
assert(animationdata->keyframe_data.size() == animationdata->keyframe_times.size());
|
||||
float vLeft = animationdata->keyframe_data[keyLeft];
|
||||
float vRight = animationdata->keyframe_data[keyRight];
|
||||
float vAnim = wi::math::Lerp(vLeft, vRight, t);
|
||||
light.outerConeAngle = vAnim;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -2985,17 +3076,73 @@ namespace wi::scene
|
||||
}
|
||||
}
|
||||
break;
|
||||
case AnimationComponent::AnimationChannel::Path::LIGHT_COLOR:
|
||||
{
|
||||
assert(animationdata->keyframe_data.size() == animationdata->keyframe_times.size() * 3 * 3);
|
||||
const XMFLOAT3* data = (const XMFLOAT3*)animationdata->keyframe_data.data();
|
||||
XMVECTOR vLeft = XMLoadFloat3(&data[keyLeft * 3 + 1]);
|
||||
XMVECTOR vLeftTanOut = dt * XMLoadFloat3(&data[keyLeft * 3 + 2]);
|
||||
XMVECTOR vRightTanIn = dt * XMLoadFloat3(&data[keyRight * 3 + 0]);
|
||||
XMVECTOR vRight = XMLoadFloat3(&data[keyRight * 3 + 1]);
|
||||
XMVECTOR vAnim = (2 * t3 - 3 * t2 + 1) * vLeft + (t3 - 2 * t2 + t) * vLeftTanOut + (-2 * t3 + 3 * t2) * vRight + (t3 - t2) * vRightTanIn;
|
||||
XMStoreFloat3(&light.color, vAnim);
|
||||
}
|
||||
break;
|
||||
case AnimationComponent::AnimationChannel::Path::LIGHT_INTENSITY:
|
||||
{
|
||||
assert(animationdata->keyframe_data.size() == animationdata->keyframe_times.size());
|
||||
float vLeft = animationdata->keyframe_data[keyLeft * 3 + 1];
|
||||
float vLeftTanOut = animationdata->keyframe_data[keyLeft * 3 + 2];
|
||||
float vRightTanIn = animationdata->keyframe_data[keyRight * 3 + 0];
|
||||
float vRight = animationdata->keyframe_data[keyRight * 3 + 1];
|
||||
float vAnim = (2 * t3 - 3 * t2 + 1) * vLeft + (t3 - 2 * t2 + t) * vLeftTanOut + (-2 * t3 + 3 * t2) * vRight + (t3 - t2) * vRightTanIn;
|
||||
light.intensity = vAnim;
|
||||
}
|
||||
break;
|
||||
case AnimationComponent::AnimationChannel::Path::LIGHT_RANGE:
|
||||
{
|
||||
assert(animationdata->keyframe_data.size() == animationdata->keyframe_times.size());
|
||||
float vLeft = animationdata->keyframe_data[keyLeft * 3 + 1];
|
||||
float vLeftTanOut = animationdata->keyframe_data[keyLeft * 3 + 2];
|
||||
float vRightTanIn = animationdata->keyframe_data[keyRight * 3 + 0];
|
||||
float vRight = animationdata->keyframe_data[keyRight * 3 + 1];
|
||||
float vAnim = (2 * t3 - 3 * t2 + 1) * vLeft + (t3 - 2 * t2 + t) * vLeftTanOut + (-2 * t3 + 3 * t2) * vRight + (t3 - t2) * vRightTanIn;
|
||||
light.range = vAnim;
|
||||
}
|
||||
break;
|
||||
case AnimationComponent::AnimationChannel::Path::LIGHT_INNERCONE:
|
||||
{
|
||||
assert(animationdata->keyframe_data.size() == animationdata->keyframe_times.size());
|
||||
float vLeft = animationdata->keyframe_data[keyLeft * 3 + 1];
|
||||
float vLeftTanOut = animationdata->keyframe_data[keyLeft * 3 + 2];
|
||||
float vRightTanIn = animationdata->keyframe_data[keyRight * 3 + 0];
|
||||
float vRight = animationdata->keyframe_data[keyRight * 3 + 1];
|
||||
float vAnim = (2 * t3 - 3 * t2 + 1) * vLeft + (t3 - 2 * t2 + t) * vLeftTanOut + (-2 * t3 + 3 * t2) * vRight + (t3 - t2) * vRightTanIn;
|
||||
light.innerConeAngle = vAnim;
|
||||
}
|
||||
break;
|
||||
case AnimationComponent::AnimationChannel::Path::LIGHT_OUTERCONE:
|
||||
{
|
||||
assert(animationdata->keyframe_data.size() == animationdata->keyframe_times.size());
|
||||
float vLeft = animationdata->keyframe_data[keyLeft * 3 + 1];
|
||||
float vLeftTanOut = animationdata->keyframe_data[keyLeft * 3 + 2];
|
||||
float vRightTanIn = animationdata->keyframe_data[keyRight * 3 + 0];
|
||||
float vRight = animationdata->keyframe_data[keyRight * 3 + 1];
|
||||
float vAnim = (2 * t3 - 3 * t2 + 1) * vLeft + (t3 - 2 * t2 + t) * vLeftTanOut + (-2 * t3 + 3 * t2) * vRight + (t3 - t2) * vRightTanIn;
|
||||
light.outerConeAngle = vAnim;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
const float t = animation.amount;
|
||||
|
||||
if (target_transform != nullptr)
|
||||
{
|
||||
target_transform->SetDirty();
|
||||
|
||||
const float t = animation.amount;
|
||||
|
||||
const XMVECTOR aS = XMLoadFloat3(&target_transform->scale_local);
|
||||
const XMVECTOR aR = XMLoadFloat4(&target_transform->rotation_local);
|
||||
const XMVECTOR aT = XMLoadFloat3(&target_transform->translation_local);
|
||||
@@ -3015,8 +3162,6 @@ namespace wi::scene
|
||||
|
||||
if (target_mesh != nullptr)
|
||||
{
|
||||
const float t = animation.amount;
|
||||
|
||||
for (size_t j = 0; j < target_mesh->targets.size(); ++j)
|
||||
{
|
||||
target_mesh->targets[j].weight = wi::math::Lerp(target_mesh->targets[j].weight, animation.morph_weights_temp[j], t);
|
||||
@@ -3025,6 +3170,15 @@ namespace wi::scene
|
||||
target_mesh->dirty_morph = true;
|
||||
}
|
||||
|
||||
if (target_light != nullptr)
|
||||
{
|
||||
target_light->color = wi::math::Lerp(target_light->color, light.color, t);
|
||||
target_light->intensity = wi::math::Lerp(target_light->intensity, light.intensity, t);
|
||||
target_light->range = wi::math::Lerp(target_light->range, light.range, t);
|
||||
target_light->innerConeAngle = wi::math::Lerp(target_light->innerConeAngle, light.innerConeAngle, t);
|
||||
target_light->outerConeAngle = wi::math::Lerp(target_light->outerConeAngle, light.outerConeAngle, t);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (animation.IsPlaying())
|
||||
@@ -3109,6 +3263,12 @@ namespace wi::scene
|
||||
const XMVECTOR windDir = XMLoadFloat3(&weather.windDirection);
|
||||
const XMVECTOR gravity = XMVectorSet(0, -9.8f, 0, 0);
|
||||
|
||||
if (springs.GetCount() > 0)
|
||||
{
|
||||
transforms_temp.resize(transforms.GetCount());
|
||||
transforms_temp = transforms.GetComponentArray(); // make copy
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < springs.GetCount(); ++i)
|
||||
{
|
||||
SpringComponent& spring = springs[i];
|
||||
@@ -3117,31 +3277,34 @@ namespace wi::scene
|
||||
continue;
|
||||
}
|
||||
Entity entity = springs.GetEntity(i);
|
||||
TransformComponent* transform = transforms.GetComponent(entity);
|
||||
if (transform == nullptr)
|
||||
size_t transform_index = transforms.GetIndex(entity);
|
||||
if (transform_index == ~0ull)
|
||||
{
|
||||
assert(0);
|
||||
continue;
|
||||
}
|
||||
TransformComponent& transform = transforms_temp[transform_index];
|
||||
|
||||
if (spring.IsResetting())
|
||||
{
|
||||
spring.Reset(false);
|
||||
spring.center_of_mass = transform->GetPosition();
|
||||
spring.center_of_mass = transform.GetPosition();
|
||||
spring.velocity = XMFLOAT3(0, 0, 0);
|
||||
}
|
||||
|
||||
const HierarchyComponent* hier = hierarchy.GetComponent(entity);
|
||||
TransformComponent* parent_transform = hier == nullptr ? nullptr : transforms.GetComponent(hier->parentID);
|
||||
if (parent_transform != nullptr)
|
||||
size_t parent_index = hier == nullptr ? ~0ull : transforms.GetIndex(hier->parentID);
|
||||
//TransformComponent* parent_transform = hier == nullptr ? nullptr : transforms.GetComponent(hier->parentID);
|
||||
if (parent_index != ~0ull)
|
||||
{
|
||||
// Spring hierarchy resolve depends on spring component order!
|
||||
// It works best when parent spring is located before child spring!
|
||||
// It will work the other way, but results will be less convincing
|
||||
transform->UpdateTransform_Parented(*parent_transform);
|
||||
const TransformComponent& parent_transform = transforms_temp[parent_index];
|
||||
transform.UpdateTransform_Parented(parent_transform);
|
||||
}
|
||||
|
||||
const XMVECTOR position_current = transform->GetPositionV();
|
||||
const XMVECTOR position_current = transform.GetPositionV();
|
||||
XMVECTOR position_prev = XMLoadFloat3(&spring.center_of_mass);
|
||||
XMVECTOR force = (position_current - position_prev) * spring.stiffness;
|
||||
|
||||
@@ -3158,9 +3321,10 @@ namespace wi::scene
|
||||
velocity += force * dt;
|
||||
XMVECTOR position_target = position_prev + velocity * dt;
|
||||
|
||||
if (parent_transform != nullptr)
|
||||
if (parent_index != ~0ull)
|
||||
{
|
||||
const XMVECTOR position_parent = parent_transform->GetPositionV();
|
||||
TransformComponent& parent_transform = transforms_temp[parent_index];
|
||||
const XMVECTOR position_parent = parent_transform.GetPositionV();
|
||||
const XMVECTOR parent_to_child = position_current - position_parent;
|
||||
const XMVECTOR parent_to_target = position_target - position_parent;
|
||||
|
||||
@@ -3177,25 +3341,34 @@ namespace wi::scene
|
||||
const XMVECTOR axis = XMVector3Normalize(XMVector3Cross(dir_parent_to_child, dir_parent_to_target));
|
||||
const float angle = XMScalarACos(XMVectorGetX(XMVector3Dot(dir_parent_to_child, dir_parent_to_target))); // don't use std::acos!
|
||||
const XMVECTOR Q = XMQuaternionNormalize(XMQuaternionRotationNormal(axis, angle));
|
||||
TransformComponent saved_parent = *parent_transform;
|
||||
TransformComponent saved_parent = parent_transform;
|
||||
saved_parent.ApplyTransform();
|
||||
saved_parent.Rotate(Q);
|
||||
saved_parent.UpdateTransform();
|
||||
std::swap(saved_parent.world, parent_transform->world); // only store temporary result, not modifying actual local space!
|
||||
std::swap(saved_parent.world, parent_transform.world); // only store temporary result, not modifying actual local space!
|
||||
}
|
||||
|
||||
XMStoreFloat3(&spring.center_of_mass, position_target);
|
||||
velocity *= spring.damping;
|
||||
XMStoreFloat3(&spring.velocity, velocity);
|
||||
*((XMFLOAT3*)&transform->world._41) = spring.center_of_mass;
|
||||
*((XMFLOAT3*)&transform.world._41) = spring.center_of_mass;
|
||||
}
|
||||
|
||||
if (springs.GetCount() > 0)
|
||||
{
|
||||
for (size_t i = 0; i < transforms.GetCount(); ++i)
|
||||
{
|
||||
// IK shouldn't modify local space, so only update the world matrices!
|
||||
transforms[i].world = transforms_temp[i].world;
|
||||
}
|
||||
}
|
||||
}
|
||||
void Scene::RunInverseKinematicsUpdateSystem(wi::jobsystem::context& ctx)
|
||||
{
|
||||
if (inverse_kinematics.GetCount() > 0)
|
||||
{
|
||||
ik_temp.resize(transforms.GetCount());
|
||||
ik_temp = transforms.GetComponentArray(); // make copy
|
||||
transforms_temp.resize(transforms.GetCount());
|
||||
transforms_temp = transforms.GetComponentArray(); // make copy
|
||||
}
|
||||
|
||||
bool recompute_hierarchy = false;
|
||||
@@ -3214,15 +3387,15 @@ namespace wi::scene
|
||||
{
|
||||
continue;
|
||||
}
|
||||
TransformComponent* transform = &ik_temp[transform_index];
|
||||
TransformComponent* target = &ik_temp[target_index];
|
||||
TransformComponent& transform = transforms_temp[transform_index];
|
||||
TransformComponent& target = transforms_temp[target_index];
|
||||
|
||||
const XMVECTOR target_pos = target->GetPositionV();
|
||||
const XMVECTOR target_pos = target.GetPositionV();
|
||||
for (uint32_t iteration = 0; iteration < ik.iteration_count; ++iteration)
|
||||
{
|
||||
TransformComponent* stack[32] = {};
|
||||
Entity parent_entity = hier->parentID;
|
||||
TransformComponent* child_transform = transform;
|
||||
TransformComponent* child_transform = &transform;
|
||||
for (uint32_t chain = 0; chain < std::min(ik.chain_length, (uint32_t)arraysize(stack)); ++chain)
|
||||
{
|
||||
recompute_hierarchy = true; // any IK will trigger a full transform hierarchy recompute step at the end(**)
|
||||
@@ -3234,19 +3407,19 @@ namespace wi::scene
|
||||
size_t parent_index = transforms.GetIndex(parent_entity);
|
||||
if (parent_index == ~0ull)
|
||||
continue;
|
||||
TransformComponent* parent_transform = &ik_temp[parent_index];
|
||||
const XMVECTOR parent_pos = parent_transform->GetPositionV();
|
||||
const XMVECTOR dir_parent_to_ik = XMVector3Normalize(transform->GetPositionV() - parent_pos);
|
||||
TransformComponent& parent_transform = transforms_temp[parent_index];
|
||||
const XMVECTOR parent_pos = parent_transform.GetPositionV();
|
||||
const XMVECTOR dir_parent_to_ik = XMVector3Normalize(transform.GetPositionV() - parent_pos);
|
||||
const XMVECTOR dir_parent_to_target = XMVector3Normalize(target_pos - parent_pos);
|
||||
const XMVECTOR axis = XMVector3Normalize(XMVector3Cross(dir_parent_to_ik, dir_parent_to_target));
|
||||
const float angle = XMScalarACos(XMVectorGetX(XMVector3Dot(dir_parent_to_ik, dir_parent_to_target)));
|
||||
const XMVECTOR Q = XMQuaternionNormalize(XMQuaternionRotationNormal(axis, angle));
|
||||
|
||||
// parent to world space:
|
||||
parent_transform->ApplyTransform();
|
||||
parent_transform.ApplyTransform();
|
||||
// rotate parent:
|
||||
parent_transform->Rotate(Q);
|
||||
parent_transform->UpdateTransform();
|
||||
parent_transform.Rotate(Q);
|
||||
parent_transform.UpdateTransform();
|
||||
// parent back to local space (if parent has parent):
|
||||
const HierarchyComponent* hier_parent = hierarchy.GetComponent(parent_entity);
|
||||
if (hier_parent != nullptr)
|
||||
@@ -3255,15 +3428,15 @@ namespace wi::scene
|
||||
size_t parent_of_parent_index = transforms.GetIndex(parent_of_parent_entity);
|
||||
if (parent_of_parent_index != ~0ull)
|
||||
{
|
||||
const TransformComponent* transform_parent_of_parent = &ik_temp[parent_of_parent_index];
|
||||
const TransformComponent* transform_parent_of_parent = &transforms_temp[parent_of_parent_index];
|
||||
XMMATRIX parent_of_parent_inverse = XMMatrixInverse(nullptr, XMLoadFloat4x4(&transform_parent_of_parent->world));
|
||||
parent_transform->MatrixTransform(parent_of_parent_inverse);
|
||||
parent_transform.MatrixTransform(parent_of_parent_inverse);
|
||||
// Do not call UpdateTransform() here, to keep parent world matrix in world space!
|
||||
}
|
||||
}
|
||||
|
||||
// update chain from parent to children:
|
||||
const TransformComponent* recurse_parent = parent_transform;
|
||||
const TransformComponent* recurse_parent = &parent_transform;
|
||||
for (int recurse_chain = (int)chain; recurse_chain >= 0; --recurse_chain)
|
||||
{
|
||||
stack[recurse_chain]->UpdateTransform_Parented(*recurse_parent);
|
||||
@@ -3277,7 +3450,7 @@ namespace wi::scene
|
||||
}
|
||||
|
||||
// move up in the chain by one:
|
||||
child_transform = parent_transform;
|
||||
child_transform = &parent_transform;
|
||||
parent_entity = hier_parent->parentID;
|
||||
assert(chain < (uint32_t)arraysize(stack) - 1); // if this is encountered, just extend stack array size
|
||||
|
||||
@@ -3299,8 +3472,8 @@ namespace wi::scene
|
||||
size_t parent_index = transforms.GetIndex(parentcomponent.parentID);
|
||||
if (transform_index != ~0ull && parent_index != ~0ull)
|
||||
{
|
||||
TransformComponent* transform_child = &ik_temp[transform_index];
|
||||
TransformComponent* transform_parent = &ik_temp[parent_index];
|
||||
TransformComponent* transform_child = &transforms_temp[transform_index];
|
||||
TransformComponent* transform_parent = &transforms_temp[parent_index];
|
||||
transform_child->UpdateTransform_Parented(*transform_parent);
|
||||
}
|
||||
}
|
||||
@@ -3311,7 +3484,7 @@ namespace wi::scene
|
||||
for (size_t i = 0; i < transforms.GetCount(); ++i)
|
||||
{
|
||||
// IK shouldn't modify local space, so only update the world matrices!
|
||||
transforms[i].world = ik_temp[i].world;
|
||||
transforms[i].world = transforms_temp[i].world;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -845,7 +845,6 @@ namespace wi::scene
|
||||
LIGHTMAPONLY_STATIC = 1 << 3,
|
||||
};
|
||||
uint32_t _flags = EMPTY;
|
||||
XMFLOAT3 color = XMFLOAT3(1, 1, 1);
|
||||
|
||||
enum LightType
|
||||
{
|
||||
@@ -860,6 +859,8 @@ namespace wi::scene
|
||||
ENUM_FORCE_UINT32 = 0xFFFFFFFF,
|
||||
};
|
||||
LightType type = POINT;
|
||||
|
||||
XMFLOAT3 color = XMFLOAT3(1, 1, 1);
|
||||
float intensity = 1.0f; // Brightness of light in. The units that this is defined in depend on the type of light. Point and spot lights use luminous intensity in candela (lm/sr) while directional lights use illuminance in lux (lm/m2). https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_lights_punctual
|
||||
float range = 10.0f;
|
||||
float outerConeAngle = XM_PIDIV4;
|
||||
@@ -1112,6 +1113,11 @@ namespace wi::scene
|
||||
ROTATION,
|
||||
SCALE,
|
||||
WEIGHTS,
|
||||
LIGHT_COLOR,
|
||||
LIGHT_INTENSITY,
|
||||
LIGHT_RANGE,
|
||||
LIGHT_INNERCONE,
|
||||
LIGHT_OUTERCONE,
|
||||
UNKNOWN,
|
||||
TYPE_FORCE_UINT32 = 0xFFFFFFFF
|
||||
} path = TRANSLATION;
|
||||
@@ -1445,7 +1451,7 @@ namespace wi::scene
|
||||
uint32_t impostorMaterialOffset = ~0u;
|
||||
|
||||
mutable std::atomic_bool lightmap_refresh_needed{ false };
|
||||
wi::vector<TransformComponent> ik_temp;
|
||||
wi::vector<TransformComponent> transforms_temp;
|
||||
|
||||
// Ocean GPU state:
|
||||
wi::Ocean ocean;
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace wi::version
|
||||
// minor features, major updates, breaking compatibility changes
|
||||
const int minor = 71;
|
||||
// minor bug fixes, alterations, refactors, updates
|
||||
const int revision = 9;
|
||||
const int revision = 10;
|
||||
|
||||
const std::string version_string = std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(revision);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user