]> git.lizzy.rs Git - minetest.git/blob - src/client/clientenvironment.cpp
Add dynamic exposure correction (#12959)
[minetest.git] / src / client / clientenvironment.cpp
1 /*
2 Minetest
3 Copyright (C) 2010-2017 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "util/serialize.h"
21 #include "util/pointedthing.h"
22 #include "client.h"
23 #include "clientenvironment.h"
24 #include "clientsimpleobject.h"
25 #include "clientmap.h"
26 #include "scripting_client.h"
27 #include "mapblock_mesh.h"
28 #include "mtevent.h"
29 #include "collision.h"
30 #include "nodedef.h"
31 #include "profiler.h"
32 #include "raycast.h"
33 #include "voxelalgorithms.h"
34 #include "settings.h"
35 #include "shader.h"
36 #include "content_cao.h"
37 #include "porting.h"
38 #include <algorithm>
39 #include "client/renderingengine.h"
40
41 /*
42         CAOShaderConstantSetter
43 */
44
45 //! Shader constant setter for passing material emissive color to the CAO object_shader
46 class CAOShaderConstantSetter : public IShaderConstantSetter
47 {
48 public:
49         CAOShaderConstantSetter():
50                         m_emissive_color_setting("emissiveColor")
51         {}
52
53         ~CAOShaderConstantSetter() override = default;
54
55         void onSetConstants(video::IMaterialRendererServices *services) override
56         {
57                 // Ambient color
58                 video::SColorf emissive_color(m_emissive_color);
59
60                 float as_array[4] = {
61                         emissive_color.r,
62                         emissive_color.g,
63                         emissive_color.b,
64                         emissive_color.a,
65                 };
66                 m_emissive_color_setting.set(as_array, services);
67         }
68
69         void onSetMaterial(const video::SMaterial& material) override
70         {
71                 m_emissive_color = material.EmissiveColor;
72         }
73
74 private:
75         video::SColor m_emissive_color;
76         CachedPixelShaderSetting<float, 4> m_emissive_color_setting;
77 };
78
79 class CAOShaderConstantSetterFactory : public IShaderConstantSetterFactory
80 {
81 public:
82         CAOShaderConstantSetterFactory()
83         {}
84
85         virtual IShaderConstantSetter* create()
86         {
87                 return new CAOShaderConstantSetter();
88         }
89 };
90
91 /*
92         ClientEnvironment
93 */
94
95 ClientEnvironment::ClientEnvironment(ClientMap *map,
96         ITextureSource *texturesource, Client *client):
97         Environment(client),
98         m_map(map),
99         m_texturesource(texturesource),
100         m_client(client)
101 {
102         auto *shdrsrc = m_client->getShaderSource();
103         shdrsrc->addShaderConstantSetterFactory(new CAOShaderConstantSetterFactory());
104 }
105
106 ClientEnvironment::~ClientEnvironment()
107 {
108         m_ao_manager.clear();
109
110         for (auto &simple_object : m_simple_objects) {
111                 delete simple_object;
112         }
113
114         // Drop/delete map
115         m_map->drop();
116
117         delete m_local_player;
118 }
119
120 Map & ClientEnvironment::getMap()
121 {
122         return *m_map;
123 }
124
125 ClientMap & ClientEnvironment::getClientMap()
126 {
127         return *m_map;
128 }
129
130 void ClientEnvironment::setLocalPlayer(LocalPlayer *player)
131 {
132         /*
133                 It is a failure if already is a local player
134         */
135         FATAL_ERROR_IF(m_local_player != NULL,
136                 "Local player already allocated");
137
138         m_local_player = player;
139 }
140
141 void ClientEnvironment::step(float dtime)
142 {
143         /* Step time of day */
144         stepTimeOfDay(dtime);
145
146         // Get some settings
147         bool fly_allowed = m_client->checkLocalPrivilege("fly");
148         bool free_move = fly_allowed && g_settings->getBool("free_move");
149
150         // Get local player
151         LocalPlayer *lplayer = getLocalPlayer();
152         assert(lplayer);
153         // collision info queue
154         std::vector<CollisionInfo> player_collisions;
155
156         /*
157                 Get the speed the player is going
158         */
159         bool is_climbing = lplayer->is_climbing;
160
161         f32 player_speed = lplayer->getSpeed().getLength();
162
163         /*
164                 Maximum position increment
165         */
166         //f32 position_max_increment = 0.05*BS;
167         f32 position_max_increment = 0.1*BS;
168
169         // Maximum time increment (for collision detection etc)
170         // time = distance / speed
171         f32 dtime_max_increment = 1;
172         if(player_speed > 0.001)
173                 dtime_max_increment = position_max_increment / player_speed;
174
175         // Maximum time increment is 10ms or lower
176         if(dtime_max_increment > 0.01)
177                 dtime_max_increment = 0.01;
178
179         // Don't allow overly huge dtime
180         if(dtime > 0.5)
181                 dtime = 0.5;
182
183         /*
184                 Stuff that has a maximum time increment
185         */
186
187         u32 steps = ceil(dtime / dtime_max_increment);
188         f32 dtime_part = dtime / steps;
189         for (; steps > 0; --steps) {
190                 /*
191                         Local player handling
192                 */
193
194                 // Control local player
195                 lplayer->applyControl(dtime_part, this);
196
197                 // Apply physics
198                 lplayer->gravity = 0;
199                 if (!free_move) {
200                         // Gravity
201                         if (!is_climbing && !lplayer->in_liquid)
202                                 // HACK the factor 2 for gravity is arbitrary and should be removed eventually
203                                 lplayer->gravity = 2 * lplayer->movement_gravity * lplayer->physics_override.gravity;
204
205                         // Liquid floating / sinking
206                         if (!is_climbing && lplayer->in_liquid &&
207                                         !lplayer->swimming_vertical &&
208                                         !lplayer->swimming_pitch)
209                                 // HACK the factor 2 for gravity is arbitrary and should be removed eventually
210                                 lplayer->gravity = 2 * lplayer->movement_liquid_sink;
211
212                         // Movement resistance
213                         if (lplayer->move_resistance > 0) {
214                                 v3f speed = lplayer->getSpeed();
215
216                                 // How much the node's move_resistance blocks movement, ranges
217                                 // between 0 and 1. Should match the scale at which liquid_viscosity
218                                 // increase affects other liquid attributes.
219                                 static const f32 resistance_factor = 0.3f;
220
221                                 v3f d_wanted;
222                                 bool in_liquid_stable = lplayer->in_liquid_stable || lplayer->in_liquid;
223                                 if (in_liquid_stable) {
224                                         d_wanted = -speed / lplayer->movement_liquid_fluidity;
225                                 } else {
226                                         d_wanted = -speed / BS;
227                                 }
228                                 f32 dl = d_wanted.getLength();
229                                 if (in_liquid_stable) {
230                                         if (dl > lplayer->movement_liquid_fluidity_smooth)
231                                                 dl = lplayer->movement_liquid_fluidity_smooth;
232                                 }
233
234                                 dl *= (lplayer->move_resistance * resistance_factor) +
235                                         (1 - resistance_factor);
236                                 v3f d = d_wanted.normalize() * (dl * dtime_part * 100.0f);
237                                 speed += d;
238
239                                 lplayer->setSpeed(speed);
240                         }
241                 }
242
243                 /*
244                         Move the lplayer.
245                         This also does collision detection.
246                 */
247
248                 lplayer->move(dtime_part, this, position_max_increment,
249                         &player_collisions);
250         }
251
252         bool player_immortal = false;
253         f32 player_fall_factor = 1.0f;
254         GenericCAO *playercao = lplayer->getCAO();
255         if (playercao) {
256                 player_immortal = playercao->isImmortal();
257                 int addp_p = itemgroup_get(playercao->getGroups(),
258                         "fall_damage_add_percent");
259                 // convert armor group into an usable fall damage factor
260                 player_fall_factor = 1.0f + (float)addp_p / 100.0f;
261         }
262
263         for (const CollisionInfo &info : player_collisions) {
264                 v3f speed_diff = info.new_speed - info.old_speed;;
265                 // Handle only fall damage
266                 // (because otherwise walking against something in fast_move kills you)
267                 if (speed_diff.Y < 0 || info.old_speed.Y >= 0)
268                         continue;
269                 // Get rid of other components
270                 speed_diff.X = 0;
271                 speed_diff.Z = 0;
272                 f32 pre_factor = 1; // 1 hp per node/s
273                 f32 tolerance = BS*14; // 5 without damage
274                 if (info.type == COLLISION_NODE) {
275                         const ContentFeatures &f = m_client->ndef()->
276                                 get(m_map->getNode(info.node_p));
277                         // Determine fall damage modifier
278                         int addp_n = itemgroup_get(f.groups, "fall_damage_add_percent");
279                         // convert node group to an usable fall damage factor
280                         f32 node_fall_factor = 1.0f + (float)addp_n / 100.0f;
281                         // combine both player fall damage modifiers
282                         pre_factor = node_fall_factor * player_fall_factor;
283                 }
284                 float speed = pre_factor * speed_diff.getLength();
285
286                 if (speed > tolerance && !player_immortal && pre_factor > 0.0f) {
287                         f32 damage_f = (speed - tolerance) / BS;
288                         u16 damage = (u16)MYMIN(damage_f + 0.5, U16_MAX);
289                         if (damage != 0) {
290                                 damageLocalPlayer(damage, true);
291                                 m_client->getEventManager()->put(
292                                         new SimpleTriggerEvent(MtEvent::PLAYER_FALLING_DAMAGE));
293                         }
294                 }
295         }
296
297         if (m_client->modsLoaded())
298                 m_script->environment_step(dtime);
299
300         // Update lighting on local player (used for wield item)
301         u32 day_night_ratio = getDayNightRatio();
302         {
303                 // Get node at head
304
305                 // On InvalidPositionException, use this as default
306                 // (day: LIGHT_SUN, night: 0)
307                 MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0);
308
309                 v3s16 p = lplayer->getLightPosition();
310                 node_at_lplayer = m_map->getNode(p);
311
312                 u16 light = getInteriorLight(node_at_lplayer, 0, m_client->ndef());
313                 lplayer->light_color = encode_light(light, 0); // this transfers light.alpha
314                 final_color_blend(&lplayer->light_color, light, day_night_ratio);
315         }
316
317         /*
318                 Step active objects and update lighting of them
319         */
320
321         bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
322         auto cb_state = [this, dtime, update_lighting, day_night_ratio] (ClientActiveObject *cao) {
323                 // Step object
324                 cao->step(dtime, this);
325
326                 if (update_lighting)
327                         cao->updateLight(day_night_ratio);
328         };
329
330         m_ao_manager.step(dtime, cb_state);
331
332         /*
333                 Step and handle simple objects
334         */
335         g_profiler->avg("ClientEnv: CSO count [#]", m_simple_objects.size());
336         for (auto i = m_simple_objects.begin(); i != m_simple_objects.end();) {
337                 ClientSimpleObject *simple = *i;
338
339                 simple->step(dtime);
340                 if(simple->m_to_be_removed) {
341                         delete simple;
342                         i = m_simple_objects.erase(i);
343                 }
344                 else {
345                         ++i;
346                 }
347         }
348 }
349
350 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
351 {
352         m_simple_objects.push_back(simple);
353 }
354
355 GenericCAO* ClientEnvironment::getGenericCAO(u16 id)
356 {
357         ClientActiveObject *obj = getActiveObject(id);
358         if (obj && obj->getType() == ACTIVEOBJECT_TYPE_GENERIC)
359                 return (GenericCAO*) obj;
360
361         return NULL;
362 }
363
364 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
365 {
366         // Register object. If failed return zero id
367         if (!m_ao_manager.registerObject(object))
368                 return 0;
369
370         object->addToScene(m_texturesource, m_client->getSceneManager());
371
372         // Update lighting immediately
373         object->updateLight(getDayNightRatio());
374         return object->getId();
375 }
376
377 void ClientEnvironment::addActiveObject(u16 id, u8 type,
378         const std::string &init_data)
379 {
380         ClientActiveObject* obj =
381                 ClientActiveObject::create((ActiveObjectType) type, m_client, this);
382         if(obj == NULL)
383         {
384                 infostream<<"ClientEnvironment::addActiveObject(): "
385                         <<"id="<<id<<" type="<<type<<": Couldn't create object"
386                         <<std::endl;
387                 return;
388         }
389
390         obj->setId(id);
391
392         try
393         {
394                 obj->initialize(init_data);
395         }
396         catch(SerializationError &e)
397         {
398                 errorstream<<"ClientEnvironment::addActiveObject():"
399                         <<" id="<<id<<" type="<<type
400                         <<": SerializationError in initialize(): "
401                         <<e.what()
402                         <<": init_data="<<serializeJsonString(init_data)
403                         <<std::endl;
404         }
405
406         u16 new_id = addActiveObject(obj);
407         // Object initialized:
408         if ((obj = getActiveObject(new_id))) {
409                 // Final step is to update all children which are already known
410                 // Data provided by AO_CMD_SPAWN_INFANT
411                 const auto &children = obj->getAttachmentChildIds();
412                 for (auto c_id : children) {
413                         if (auto *o = getActiveObject(c_id))
414                                 o->updateAttachments();
415                 }
416         }
417 }
418
419
420 void ClientEnvironment::removeActiveObject(u16 id)
421 {
422         // Get current attachment childs to detach them visually
423         std::unordered_set<int> attachment_childs;
424         if (auto *obj = getActiveObject(id))
425                 attachment_childs = obj->getAttachmentChildIds();
426
427         m_ao_manager.removeObject(id);
428
429         // Perform a proper detach in Irrlicht
430         for (auto c_id : attachment_childs) {
431                 if (ClientActiveObject *child = getActiveObject(c_id))
432                         child->updateAttachments();
433         }
434 }
435
436 void ClientEnvironment::processActiveObjectMessage(u16 id, const std::string &data)
437 {
438         ClientActiveObject *obj = getActiveObject(id);
439         if (obj == NULL) {
440                 infostream << "ClientEnvironment::processActiveObjectMessage():"
441                         << " got message for id=" << id << ", which doesn't exist."
442                         << std::endl;
443                 return;
444         }
445
446         try {
447                 obj->processMessage(data);
448         } catch (SerializationError &e) {
449                 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
450                         << " id=" << id << " type=" << obj->getType()
451                         << " SerializationError in processMessage(): " << e.what()
452                         << std::endl;
453         }
454 }
455
456 /*
457         Callbacks for activeobjects
458 */
459
460 void ClientEnvironment::damageLocalPlayer(u16 damage, bool handle_hp)
461 {
462         LocalPlayer *lplayer = getLocalPlayer();
463         assert(lplayer);
464
465         if (handle_hp) {
466                 if (lplayer->hp > damage)
467                         lplayer->hp -= damage;
468                 else
469                         lplayer->hp = 0;
470         }
471
472         ClientEnvEvent event;
473         event.type = CEE_PLAYER_DAMAGE;
474         event.player_damage.amount = damage;
475         event.player_damage.send_to_server = handle_hp;
476         m_client_event_queue.push(event);
477 }
478
479 /*
480         Client likes to call these
481 */
482
483 ClientEnvEvent ClientEnvironment::getClientEnvEvent()
484 {
485         FATAL_ERROR_IF(m_client_event_queue.empty(),
486                         "ClientEnvironment::getClientEnvEvent(): queue is empty");
487
488         ClientEnvEvent event = m_client_event_queue.front();
489         m_client_event_queue.pop();
490         return event;
491 }
492
493 void ClientEnvironment::getSelectedActiveObjects(
494         const core::line3d<f32> &shootline_on_map,
495         std::vector<PointedThing> &objects)
496 {
497         std::vector<DistanceSortedActiveObject> allObjects;
498         m_ao_manager.getActiveSelectableObjects(shootline_on_map, allObjects);
499         const v3f line_vector = shootline_on_map.getVector();
500
501         for (const auto &allObject : allObjects) {
502                 ClientActiveObject *obj = allObject.obj;
503                 aabb3f selection_box;
504                 if (!obj->getSelectionBox(&selection_box))
505                         continue;
506
507                 v3f current_intersection;
508                 v3f current_normal, current_raw_normal;
509                 const v3f rel_pos = shootline_on_map.start - obj->getPosition();
510                 bool collision;
511                 GenericCAO* gcao = dynamic_cast<GenericCAO*>(obj);
512                 if (gcao != nullptr && gcao->getProperties().rotate_selectionbox) {
513                         gcao->getSceneNode()->updateAbsolutePosition();
514                         const v3f deg = obj->getSceneNode()->getAbsoluteTransformation().getRotationDegrees();
515                         collision = boxLineCollision(selection_box, deg,
516                                 rel_pos, line_vector, &current_intersection, &current_normal, &current_raw_normal);
517                 } else {
518                         collision = boxLineCollision(selection_box, rel_pos, line_vector,
519                                 &current_intersection, &current_normal);
520                         current_raw_normal = current_normal;
521                 }
522                 if (collision) {
523                         current_intersection += obj->getPosition();
524                         objects.emplace_back(obj->getId(), current_intersection, current_normal, current_raw_normal,
525                                 (current_intersection - shootline_on_map.start).getLengthSQ());
526                 }
527         }
528 }
529
530 void ClientEnvironment::updateFrameTime(bool is_paused)
531 {
532         // if paused, m_frame_time_pause_accumulator increases by dtime,
533         // otherwise, m_frame_time increases by dtime
534         if (is_paused) {
535                 m_frame_dtime = 0;
536                 m_frame_time_pause_accumulator = porting::getTimeMs() - m_frame_time;
537         }
538         else {
539                 auto new_frame_time = porting::getTimeMs() - m_frame_time_pause_accumulator;
540                 m_frame_dtime = new_frame_time - MYMAX(m_frame_time, m_frame_time_pause_accumulator);
541                 m_frame_time = new_frame_time;
542         }
543 }