ContentCAO: Update light of all attached entities (#9975)
[oweals/minetest.git] / src / client / clientenvironment.cpp
index 7c2ec099ce017fff7f4afc37433e7838cd4aef9f..44ea1e4a157a53282300c203988bc1e2c9177790 100644 (file)
@@ -32,10 +32,65 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #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,
+                       bool is_highlevel) override
+       {
+               if (!is_highlevel)
+                       return;
+
+               // 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
 */
@@ -47,8 +102,8 @@ ClientEnvironment::ClientEnvironment(ClientMap *map,
        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()
@@ -170,7 +225,8 @@ void ClientEnvironment::step(float dtime)
                                                lplayer->physics_override_gravity * dtime_part * 2.0f;
 
                                // Liquid floating / sinking
-                               if (lplayer->in_liquid && !lplayer->swimming_vertical)
+                               if (lplayer->in_liquid && !lplayer->swimming_vertical &&
+                                               !lplayer->swimming_pitch)
                                        speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2.0f;
 
                                // Liquid resistance
@@ -219,7 +275,7 @@ void ClientEnvironment::step(float dtime)
                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));
+                               get(m_map->getNode(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;
@@ -227,7 +283,7 @@ void ClientEnvironment::step(float dtime)
                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);
+                       u16 damage = (u16)MYMIN(damage_f + 0.5, U16_MAX);
                        if (damage != 0) {
                                damageLocalPlayer(damage, true);
                                m_client->getEventManager()->put(
@@ -249,7 +305,7 @@ void ClientEnvironment::step(float dtime)
                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);
@@ -264,21 +320,8 @@ void ClientEnvironment::step(float dtime)
                // 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);
@@ -286,15 +329,14 @@ void ClientEnvironment::step(float dtime)
        /*
                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;
@@ -347,18 +389,7 @@ u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
        object->addToScene(m_texturesource);
 
        // 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();
 }
 
@@ -391,7 +422,34 @@ void ClientEnvironment::addActiveObject(u16 id, u8 type,
                        <<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)
@@ -418,7 +476,7 @@ void ClientEnvironment::processActiveObjectMessage(u16 id, const std::string &da
        Callbacks for activeobjects
 */
 
-void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
+void ClientEnvironment::damageLocalPlayer(u16 damage, bool handle_hp)
 {
        LocalPlayer *lplayer = getLocalPlayer();
        assert(lplayer);