#include "clientmap.h"
#include "scripting_client.h"
#include "mapblock_mesh.h"
-#include "event.h"
+#include "mtevent.h"
#include "collision.h"
#include "nodedef.h"
#include "profiler.h"
#include "raycast.h"
#include "voxelalgorithms.h"
#include "settings.h"
+#include "shader.h"
#include "content_cao.h"
#include <algorithm>
#include "client/renderingengine.h"
+/*
+ CAOShaderConstantSetter
+*/
+
+//! Shader constant setter for passing material emissive color to the CAO object_shader
+class CAOShaderConstantSetter : public IShaderConstantSetter
+{
+public:
+ CAOShaderConstantSetter():
+ m_emissive_color_setting("emissiveColor")
+ {}
+
+ ~CAOShaderConstantSetter() override = default;
+
+ void onSetConstants(video::IMaterialRendererServices *services) override
+ {
+ // Ambient color
+ video::SColorf emissive_color(m_emissive_color);
+
+ float as_array[4] = {
+ emissive_color.r,
+ emissive_color.g,
+ emissive_color.b,
+ emissive_color.a,
+ };
+ m_emissive_color_setting.set(as_array, services);
+ }
+
+ void onSetMaterial(const video::SMaterial& material) override
+ {
+ m_emissive_color = material.EmissiveColor;
+ }
+
+private:
+ video::SColor m_emissive_color;
+ CachedPixelShaderSetting<float, 4> m_emissive_color_setting;
+};
+
+class CAOShaderConstantSetterFactory : public IShaderConstantSetterFactory
+{
+public:
+ CAOShaderConstantSetterFactory()
+ {}
+
+ virtual IShaderConstantSetter* create()
+ {
+ return new CAOShaderConstantSetter();
+ }
+};
+
/*
ClientEnvironment
*/
m_texturesource(texturesource),
m_client(client)
{
- char zero = 0;
- memset(attachement_parent_ids, zero, sizeof(attachement_parent_ids));
+ auto *shdrsrc = m_client->getShaderSource();
+ shdrsrc->addShaderConstantSetterFactory(new CAOShaderConstantSetterFactory());
}
ClientEnvironment::~ClientEnvironment()
if(dtime > 0.5)
dtime = 0.5;
- f32 dtime_downcount = dtime;
-
/*
Stuff that has a maximum time increment
*/
- u32 loopcount = 0;
- do
- {
- loopcount++;
-
- f32 dtime_part;
- if(dtime_downcount > dtime_max_increment)
- {
- dtime_part = dtime_max_increment;
- dtime_downcount -= dtime_part;
- }
- else
- {
- dtime_part = dtime_downcount;
- /*
- Setting this to 0 (no -=dtime_part) disables an infinite loop
- when dtime_part is so small that dtime_downcount -= dtime_part
- does nothing
- */
- dtime_downcount = 0;
- }
-
+ u32 steps = ceil(dtime / dtime_max_increment);
+ f32 dtime_part = dtime / steps;
+ for (; steps > 0; --steps) {
/*
- Handle local player
+ Local player handling
*/
- {
- // Apply physics
- if (!free_move && !is_climbing) {
- // Gravity
- v3f speed = lplayer->getSpeed();
- if (!lplayer->in_liquid)
- speed.Y -= lplayer->movement_gravity *
- lplayer->physics_override_gravity * dtime_part * 2.0f;
-
- // Liquid floating / sinking
- if (lplayer->in_liquid && !lplayer->swimming_vertical)
- speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2.0f;
-
- // Liquid resistance
- if (lplayer->in_liquid_stable || lplayer->in_liquid) {
- // How much the node's viscosity blocks movement, ranges
- // between 0 and 1. Should match the scale at which viscosity
- // increase affects other liquid attributes.
- static const f32 viscosity_factor = 0.3f;
-
- v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
- f32 dl = d_wanted.getLength();
+ // Control local player
+ lplayer->applyControl(dtime_part, this);
+
+ // Apply physics
+ if (!free_move) {
+ // Gravity
+ v3f speed = lplayer->getSpeed();
+ if (!is_climbing && !lplayer->in_liquid)
+ speed.Y -= lplayer->movement_gravity *
+ lplayer->physics_override_gravity * dtime_part * 2.0f;
+
+ // Liquid floating / sinking
+ if (!is_climbing && lplayer->in_liquid &&
+ !lplayer->swimming_vertical &&
+ !lplayer->swimming_pitch)
+ speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2.0f;
+
+ // Movement resistance
+ if (lplayer->move_resistance > 0) {
+ // How much the node's move_resistance blocks movement, ranges
+ // between 0 and 1. Should match the scale at which liquid_viscosity
+ // increase affects other liquid attributes.
+ static const f32 resistance_factor = 0.3f;
+
+ v3f d_wanted;
+ bool in_liquid_stable = lplayer->in_liquid_stable || lplayer->in_liquid;
+ if (in_liquid_stable) {
+ d_wanted = -speed / lplayer->movement_liquid_fluidity;
+ } else {
+ d_wanted = -speed / BS;
+ }
+ f32 dl = d_wanted.getLength();
+ if (in_liquid_stable) {
if (dl > lplayer->movement_liquid_fluidity_smooth)
dl = lplayer->movement_liquid_fluidity_smooth;
-
- dl *= (lplayer->liquid_viscosity * viscosity_factor) +
- (1 - viscosity_factor);
- v3f d = d_wanted.normalize() * (dl * dtime_part * 100.0f);
- speed += d;
}
- lplayer->setSpeed(speed);
+ dl *= (lplayer->move_resistance * resistance_factor) +
+ (1 - resistance_factor);
+ v3f d = d_wanted.normalize() * (dl * dtime_part * 100.0f);
+ speed += d;
}
- /*
- Move the lplayer.
- This also does collision detection.
- */
- lplayer->move(dtime_part, this, position_max_increment,
- &player_collisions);
+ lplayer->setSpeed(speed);
}
- } while (dtime_downcount > 0.001);
- bool player_immortal = lplayer->getCAO() && lplayer->getCAO()->isImmortal();
+ /*
+ Move the lplayer.
+ This also does collision detection.
+ */
+ lplayer->move(dtime_part, this, position_max_increment,
+ &player_collisions);
+ }
+
+ bool player_immortal = false;
+ f32 player_fall_factor = 1.0f;
+ GenericCAO *playercao = lplayer->getCAO();
+ if (playercao) {
+ player_immortal = playercao->isImmortal();
+ int addp_p = itemgroup_get(playercao->getGroups(),
+ "fall_damage_add_percent");
+ // convert armor group into an usable fall damage factor
+ player_fall_factor = 1.0f + (float)addp_p / 100.0f;
+ }
for (const CollisionInfo &info : player_collisions) {
v3f speed_diff = info.new_speed - info.old_speed;;
speed_diff.Z = 0;
f32 pre_factor = 1; // 1 hp per node/s
f32 tolerance = BS*14; // 5 without damage
- f32 post_factor = 1; // 1 hp per node/s
if (info.type == COLLISION_NODE) {
const ContentFeatures &f = m_client->ndef()->
- get(m_map->getNodeNoEx(info.node_p));
- // Determine fall damage multiplier
- int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
- pre_factor = 1.0f + (float)addp / 100.0f;
+ get(m_map->getNode(info.node_p));
+ // Determine fall damage modifier
+ int addp_n = itemgroup_get(f.groups, "fall_damage_add_percent");
+ // convert node group to an usable fall damage factor
+ f32 node_fall_factor = 1.0f + (float)addp_n / 100.0f;
+ // combine both player fall damage modifiers
+ pre_factor = node_fall_factor * player_fall_factor;
}
float speed = pre_factor * speed_diff.getLength();
- if (speed > tolerance && !player_immortal) {
- f32 damage_f = (speed - tolerance) / BS * post_factor;
- u8 damage = (u8)MYMIN(damage_f + 0.5, 255);
+
+ if (speed > tolerance && !player_immortal && pre_factor > 0.0f) {
+ f32 damage_f = (speed - tolerance) / BS;
+ u16 damage = (u16)MYMIN(damage_f + 0.5, U16_MAX);
if (damage != 0) {
damageLocalPlayer(damage, true);
m_client->getEventManager()->put(
MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0);
v3s16 p = lplayer->getLightPosition();
- node_at_lplayer = m_map->getNodeNoEx(p);
+ node_at_lplayer = m_map->getNode(p);
u16 light = getInteriorLight(node_at_lplayer, 0, m_client->ndef());
final_color_blend(&lplayer->light_color, light, day_night_ratio);
// Step object
cao->step(dtime, this);
- if (update_lighting) {
- // Update lighting
- u8 light = 0;
- bool pos_ok;
-
- // Get node at head
- v3s16 p = cao->getLightPosition();
- MapNode n = this->m_map->getNodeNoEx(p, &pos_ok);
- if (pos_ok)
- light = n.getLightBlend(day_night_ratio, m_client->ndef());
- else
- light = blend_light(day_night_ratio, LIGHT_SUN, 0);
-
- cao->updateLight(light);
- }
+ if (update_lighting)
+ cao->updateLight(day_night_ratio);
};
m_ao_manager.step(dtime, cb_state);
/*
Step and handle simple objects
*/
- g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
+ g_profiler->avg("ClientEnv: CSO count [#]", m_simple_objects.size());
for (auto i = m_simple_objects.begin(); i != m_simple_objects.end();) {
- auto cur = i;
- ClientSimpleObject *simple = *cur;
+ ClientSimpleObject *simple = *i;
simple->step(dtime);
if(simple->m_to_be_removed) {
delete simple;
- i = m_simple_objects.erase(cur);
+ i = m_simple_objects.erase(i);
}
else {
++i;
return NULL;
}
-bool isFreeClientActiveObjectId(const u16 id,
- ClientActiveObjectMap &objects)
-{
- return id != 0 && objects.find(id) == objects.end();
-
-}
-
-u16 getFreeClientActiveObjectId(ClientActiveObjectMap &objects)
-{
- // try to reuse id's as late as possible
- static u16 last_used_id = 0;
- u16 startid = last_used_id;
- for(;;) {
- last_used_id ++;
- if (isFreeClientActiveObjectId(last_used_id, objects))
- return last_used_id;
-
- if (last_used_id == startid)
- return 0;
- }
-}
-
u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
{
// Register object. If failed return zero id
if (!m_ao_manager.registerObject(object))
return 0;
- object->addToScene(m_texturesource);
+ object->addToScene(m_texturesource, m_client->getSceneManager());
// Update lighting immediately
- u8 light = 0;
- bool pos_ok;
-
- // Get node at head
- v3s16 p = object->getLightPosition();
- MapNode n = m_map->getNodeNoEx(p, &pos_ok);
- if (pos_ok)
- light = n.getLightBlend(getDayNightRatio(), m_client->ndef());
- else
- light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
-
- object->updateLight(light);
+ object->updateLight(getDayNightRatio());
return object->getId();
}
<<std::endl;
}
- addActiveObject(obj);
+ u16 new_id = addActiveObject(obj);
+ // Object initialized:
+ if ((obj = getActiveObject(new_id))) {
+ // Final step is to update all children which are already known
+ // Data provided by AO_CMD_SPAWN_INFANT
+ const auto &children = obj->getAttachmentChildIds();
+ for (auto c_id : children) {
+ if (auto *o = getActiveObject(c_id))
+ o->updateAttachments();
+ }
+ }
+}
+
+
+void ClientEnvironment::removeActiveObject(u16 id)
+{
+ // Get current attachment childs to detach them visually
+ std::unordered_set<int> attachment_childs;
+ if (auto *obj = getActiveObject(id))
+ attachment_childs = obj->getAttachmentChildIds();
+
+ m_ao_manager.removeObject(id);
+
+ // Perform a proper detach in Irrlicht
+ for (auto c_id : attachment_childs) {
+ if (ClientActiveObject *child = getActiveObject(c_id))
+ child->updateAttachments();
+ }
}
void ClientEnvironment::processActiveObjectMessage(u16 id, const std::string &data)
Callbacks for activeobjects
*/
-void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
+void ClientEnvironment::damageLocalPlayer(u16 damage, bool handle_hp)
{
LocalPlayer *lplayer = getLocalPlayer();
assert(lplayer);