From eb2c19bbedecb5f42c946b0c14466abc0b109825 Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Sun, 8 Jan 2017 10:49:47 +0100 Subject: Move ClientEnvironment to dedicated cpp/header files --- src/clientenvironment.cpp | 847 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 847 insertions(+) create mode 100644 src/clientenvironment.cpp (limited to 'src/clientenvironment.cpp') diff --git a/src/clientenvironment.cpp b/src/clientenvironment.cpp new file mode 100644 index 000000000..e831de109 --- /dev/null +++ b/src/clientenvironment.cpp @@ -0,0 +1,847 @@ +/* +Minetest +Copyright (C) 2010-2017 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "util/serialize.h" +#include "util/pointedthing.h" +#include "clientenvironment.h" +#include "clientsimpleobject.h" +#include "clientmap.h" +#include "mapblock_mesh.h" +#include "event.h" +#include "collision.h" +#include "profiler.h" +#include "raycast.h" +#include "voxelalgorithms.h" + +/* + ClientEnvironment +*/ + +ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr, + ITextureSource *texturesource, IGameDef *gamedef, + IrrlichtDevice *irr): + m_map(map), + m_local_player(NULL), + m_smgr(smgr), + m_texturesource(texturesource), + m_gamedef(gamedef), + m_irr(irr) +{ + char zero = 0; + memset(attachement_parent_ids, zero, sizeof(attachement_parent_ids)); +} + +ClientEnvironment::~ClientEnvironment() +{ + // delete active objects + for (UNORDERED_MAP::iterator i = m_active_objects.begin(); + i != m_active_objects.end(); ++i) { + delete i->second; + } + + for(std::vector::iterator + i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i) { + delete *i; + } + + // Drop/delete map + m_map->drop(); +} + +Map & ClientEnvironment::getMap() +{ + return *m_map; +} + +ClientMap & ClientEnvironment::getClientMap() +{ + return *m_map; +} + +void ClientEnvironment::setLocalPlayer(LocalPlayer *player) +{ + DSTACK(FUNCTION_NAME); + /* + It is a failure if already is a local player + */ + FATAL_ERROR_IF(m_local_player != NULL, + "Local player already allocated"); + + m_local_player = player; +} + +void ClientEnvironment::step(float dtime) +{ + DSTACK(FUNCTION_NAME); + + /* Step time of day */ + stepTimeOfDay(dtime); + + // Get some settings + bool fly_allowed = m_gamedef->checkLocalPrivilege("fly"); + bool free_move = fly_allowed && g_settings->getBool("free_move"); + + // Get local player + LocalPlayer *lplayer = getLocalPlayer(); + assert(lplayer); + // collision info queue + std::vector player_collisions; + + /* + Get the speed the player is going + */ + bool is_climbing = lplayer->is_climbing; + + f32 player_speed = lplayer->getSpeed().getLength(); + + /* + Maximum position increment + */ + //f32 position_max_increment = 0.05*BS; + f32 position_max_increment = 0.1*BS; + + // Maximum time increment (for collision detection etc) + // time = distance / speed + f32 dtime_max_increment = 1; + if(player_speed > 0.001) + dtime_max_increment = position_max_increment / player_speed; + + // Maximum time increment is 10ms or lower + if(dtime_max_increment > 0.01) + dtime_max_increment = 0.01; + + // Don't allow overly huge dtime + 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; + } + + /* + Handle local player + */ + + { + // 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; + + // Liquid floating / sinking + if(lplayer->in_liquid && !lplayer->swimming_vertical) + speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2; + + // 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 + const f32 viscosity_factor = 0.3; + + v3f d_wanted = -speed / lplayer->movement_liquid_fluidity; + f32 dl = d_wanted.getLength(); + 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; + speed += d; + } + + lplayer->setSpeed(speed); + } + + /* + Move the lplayer. + This also does collision detection. + */ + lplayer->move(dtime_part, this, position_max_increment, + &player_collisions); + } + } + while(dtime_downcount > 0.001); + + //std::cout<<"Looped "<::iterator i = player_collisions.begin(); + i != player_collisions.end(); ++i) { + CollisionInfo &info = *i; + v3f speed_diff = info.new_speed - info.old_speed;; + // Handle only fall damage + // (because otherwise walking against something in fast_move kills you) + if(speed_diff.Y < 0 || info.old_speed.Y >= 0) + continue; + // Get rid of other components + speed_diff.X = 0; + 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_gamedef->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.0 + (float)addp/100.0; + } + float speed = pre_factor * speed_diff.getLength(); + if(speed > tolerance) + { + f32 damage_f = (speed - tolerance)/BS * post_factor; + u16 damage = (u16)(damage_f+0.5); + if(damage != 0){ + damageLocalPlayer(damage, true); + MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage"); + m_gamedef->event()->put(e); + } + } + } + + /* + A quick draft of lava damage + */ + if(m_lava_hurt_interval.step(dtime, 1.0)) + { + v3f pf = lplayer->getPosition(); + + // Feet, middle and head + v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS); + MapNode n1 = m_map->getNodeNoEx(p1); + v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS); + MapNode n2 = m_map->getNodeNoEx(p2); + v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS); + MapNode n3 = m_map->getNodeNoEx(p3); + + u32 damage_per_second = 0; + damage_per_second = MYMAX(damage_per_second, + m_gamedef->ndef()->get(n1).damage_per_second); + damage_per_second = MYMAX(damage_per_second, + m_gamedef->ndef()->get(n2).damage_per_second); + damage_per_second = MYMAX(damage_per_second, + m_gamedef->ndef()->get(n3).damage_per_second); + + if(damage_per_second != 0) + { + damageLocalPlayer(damage_per_second, true); + } + } + + // Protocol v29 make this behaviour obsolete + if (((Client*) getGameDef())->getProtoVersion() < 29) { + /* + Drowning + */ + if (m_drowning_interval.step(dtime, 2.0)) { + v3f pf = lplayer->getPosition(); + + // head + v3s16 p = floatToInt(pf + v3f(0, BS * 1.6, 0), BS); + MapNode n = m_map->getNodeNoEx(p); + ContentFeatures c = m_gamedef->ndef()->get(n); + u8 drowning_damage = c.drowning; + if (drowning_damage > 0 && lplayer->hp > 0) { + u16 breath = lplayer->getBreath(); + if (breath > 10) { + breath = 11; + } + if (breath > 0) { + breath -= 1; + } + lplayer->setBreath(breath); + updateLocalPlayerBreath(breath); + } + + if (lplayer->getBreath() == 0 && drowning_damage > 0) { + damageLocalPlayer(drowning_damage, true); + } + } + if (m_breathing_interval.step(dtime, 0.5)) { + v3f pf = lplayer->getPosition(); + + // head + v3s16 p = floatToInt(pf + v3f(0, BS * 1.6, 0), BS); + MapNode n = m_map->getNodeNoEx(p); + ContentFeatures c = m_gamedef->ndef()->get(n); + if (!lplayer->hp) { + lplayer->setBreath(11); + } else if (c.drowning == 0) { + u16 breath = lplayer->getBreath(); + if (breath <= 10) { + breath += 1; + lplayer->setBreath(breath); + updateLocalPlayerBreath(breath); + } + } + } + } + + // Update lighting on local player (used for wield item) + u32 day_night_ratio = getDayNightRatio(); + { + // Get node at head + + // On InvalidPositionException, use this as default + // (day: LIGHT_SUN, night: 0) + MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0); + + v3s16 p = lplayer->getLightPosition(); + node_at_lplayer = m_map->getNodeNoEx(p); + + u16 light = getInteriorLight(node_at_lplayer, 0, m_gamedef->ndef()); + u8 day = light & 0xff; + u8 night = (light >> 8) & 0xff; + finalColorBlend(lplayer->light_color, day, night, day_night_ratio); + } + + /* + Step active objects and update lighting of them + */ + + g_profiler->avg("CEnv: num of objects", m_active_objects.size()); + bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21); + for (UNORDERED_MAP::iterator i = m_active_objects.begin(); + i != m_active_objects.end(); ++i) { + ClientActiveObject* obj = i->second; + // Step object + obj->step(dtime, this); + + if(update_lighting) + { + // Update lighting + u8 light = 0; + bool pos_ok; + + // Get node at head + v3s16 p = obj->getLightPosition(); + MapNode n = m_map->getNodeNoEx(p, &pos_ok); + if (pos_ok) + light = n.getLightBlend(day_night_ratio, m_gamedef->ndef()); + else + light = blend_light(day_night_ratio, LIGHT_SUN, 0); + + obj->updateLight(light); + } + } + + /* + Step and handle simple objects + */ + g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size()); + for(std::vector::iterator + i = m_simple_objects.begin(); i != m_simple_objects.end();) { + std::vector::iterator cur = i; + ClientSimpleObject *simple = *cur; + + simple->step(dtime); + if(simple->m_to_be_removed) { + delete simple; + i = m_simple_objects.erase(cur); + } + else { + ++i; + } + } +} + +void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple) +{ + m_simple_objects.push_back(simple); +} + +GenericCAO* ClientEnvironment::getGenericCAO(u16 id) +{ + ClientActiveObject *obj = getActiveObject(id); + if (obj && obj->getType() == ACTIVEOBJECT_TYPE_GENERIC) + return (GenericCAO*) obj; + else + return NULL; +} + +ClientActiveObject* ClientEnvironment::getActiveObject(u16 id) +{ + UNORDERED_MAP::iterator n = m_active_objects.find(id); + if (n == m_active_objects.end()) + return NULL; + return n->second; +} + +bool isFreeClientActiveObjectId(const u16 id, + UNORDERED_MAP &objects) +{ + if(id == 0) + return false; + + return objects.find(id) == objects.end(); +} + +u16 getFreeClientActiveObjectId(UNORDERED_MAP &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) +{ + assert(object); // Pre-condition + if(object->getId() == 0) + { + u16 new_id = getFreeClientActiveObjectId(m_active_objects); + if(new_id == 0) + { + infostream<<"ClientEnvironment::addActiveObject(): " + <<"no free ids available"<setId(new_id); + } + if (!isFreeClientActiveObjectId(object->getId(), m_active_objects)) { + infostream<<"ClientEnvironment::addActiveObject(): " + <<"id is not free ("<getId()<<")"<getId()] = object; + object->addToScene(m_smgr, m_texturesource, m_irr); + { // 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_gamedef->ndef()); + else + light = blend_light(getDayNightRatio(), LIGHT_SUN, 0); + + object->updateLight(light); + } + return object->getId(); +} + +void ClientEnvironment::addActiveObject(u16 id, u8 type, + const std::string &init_data) +{ + ClientActiveObject* obj = + ClientActiveObject::create((ActiveObjectType) type, m_gamedef, this); + if(obj == NULL) + { + infostream<<"ClientEnvironment::addActiveObject(): " + <<"id="<setId(id); + + try + { + obj->initialize(init_data); + } + catch(SerializationError &e) + { + errorstream<<"ClientEnvironment::addActiveObject():" + <<" id="<removeFromScene(true); + delete obj; + m_active_objects.erase(id); +} + +void ClientEnvironment::processActiveObjectMessage(u16 id, const std::string &data) +{ + ClientActiveObject *obj = getActiveObject(id); + if (obj == NULL) { + infostream << "ClientEnvironment::processActiveObjectMessage():" + << " got message for id=" << id << ", which doesn't exist." + << std::endl; + return; + } + + try { + obj->processMessage(data); + } catch (SerializationError &e) { + errorstream<<"ClientEnvironment::processActiveObjectMessage():" + << " id=" << id << " type=" << obj->getType() + << " SerializationError in processMessage(): " << e.what() + << std::endl; + } +} + +/* + Callbacks for activeobjects +*/ + +void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp) +{ + LocalPlayer *lplayer = getLocalPlayer(); + assert(lplayer); + + if (handle_hp) { + if (lplayer->hp > damage) + lplayer->hp -= damage; + else + lplayer->hp = 0; + } + + ClientEnvEvent event; + event.type = CEE_PLAYER_DAMAGE; + event.player_damage.amount = damage; + event.player_damage.send_to_server = handle_hp; + m_client_event_queue.push(event); +} + +void ClientEnvironment::updateLocalPlayerBreath(u16 breath) +{ + ClientEnvEvent event; + event.type = CEE_PLAYER_BREATH; + event.player_breath.amount = breath; + m_client_event_queue.push(event); +} + +/* + Client likes to call these +*/ + +void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d, + std::vector &dest) +{ + for (UNORDERED_MAP::iterator i = m_active_objects.begin(); + i != m_active_objects.end(); ++i) { + ClientActiveObject* obj = i->second; + + f32 d = (obj->getPosition() - origin).getLength(); + + if(d > max_d) + continue; + + DistanceSortedActiveObject dso(obj, d); + + dest.push_back(dso); + } +} + +ClientEnvEvent ClientEnvironment::getClientEvent() +{ + ClientEnvEvent event; + if(m_client_event_queue.empty()) + event.type = CEE_NONE; + else { + event = m_client_event_queue.front(); + m_client_event_queue.pop(); + } + return event; +} + +ClientActiveObject * ClientEnvironment::getSelectedActiveObject( + const core::line3d &shootline_on_map, v3f *intersection_point, + v3s16 *intersection_normal) +{ + std::vector objects; + getActiveObjects(shootline_on_map.start, + shootline_on_map.getLength() + 3, objects); + const v3f line_vector = shootline_on_map.getVector(); + + // Sort them. + // After this, the closest object is the first in the array. + std::sort(objects.begin(), objects.end()); + + /* Because objects can have different nodebox sizes, + * the object whose center is the nearest isn't necessarily + * the closest one. If an object is found, don't stop + * immediately. */ + + f32 d_min = shootline_on_map.getLength(); + ClientActiveObject *nearest_obj = NULL; + for (u32 i = 0; i < objects.size(); i++) { + ClientActiveObject *obj = objects[i].obj; + + aabb3f *selection_box = obj->getSelectionBox(); + if (selection_box == NULL) + continue; + + v3f pos = obj->getPosition(); + + aabb3f offsetted_box(selection_box->MinEdge + pos, + selection_box->MaxEdge + pos); + + if (offsetted_box.getCenter().getDistanceFrom( + shootline_on_map.start) > d_min + 9.6f*BS) { + // Probably there is no active object that has bigger nodebox than + // (-5.5,-5.5,-5.5,5.5,5.5,5.5) + // 9.6 > 5.5*sqrt(3) + break; + } + + v3f current_intersection; + v3s16 current_normal; + if (boxLineCollision(offsetted_box, shootline_on_map.start, line_vector, + ¤t_intersection, ¤t_normal)) { + f32 d_current = current_intersection.getDistanceFrom( + shootline_on_map.start); + if (d_current <= d_min) { + d_min = d_current; + nearest_obj = obj; + *intersection_point = current_intersection; + *intersection_normal = current_normal; + } + } + } + + return nearest_obj; +} + +/* + Check if a node is pointable +*/ +static inline bool isPointableNode(const MapNode &n, + INodeDefManager *ndef, bool liquids_pointable) +{ + const ContentFeatures &features = ndef->get(n); + return features.pointable || + (liquids_pointable && features.isLiquid()); +} + +PointedThing ClientEnvironment::getPointedThing( + core::line3d shootline, + bool liquids_pointable, + bool look_for_object) +{ + PointedThing result; + + INodeDefManager *nodedef = m_map->getNodeDefManager(); + + core::aabbox3d maximal_exceed = nodedef->getSelectionBoxIntUnion(); + // The code needs to search these nodes + core::aabbox3d search_range(-maximal_exceed.MaxEdge, + -maximal_exceed.MinEdge); + // If a node is found, there might be a larger node behind. + // To find it, we have to go further. + s16 maximal_overcheck = + std::max(abs(search_range.MinEdge.X), abs(search_range.MaxEdge.X)) + + std::max(abs(search_range.MinEdge.Y), abs(search_range.MaxEdge.Y)) + + std::max(abs(search_range.MinEdge.Z), abs(search_range.MaxEdge.Z)); + + const v3f original_vector = shootline.getVector(); + const f32 original_length = original_vector.getLength(); + + f32 min_distance = original_length; + + // First try to find an active object + if (look_for_object) { + ClientActiveObject *selected_object = getSelectedActiveObject( + shootline, &result.intersection_point, + &result.intersection_normal); + + if (selected_object != NULL) { + min_distance = + (result.intersection_point - shootline.start).getLength(); + + result.type = POINTEDTHING_OBJECT; + result.object_id = selected_object->getId(); + } + } + + // Reduce shootline + if (original_length > 0) { + shootline.end = shootline.start + + shootline.getVector() / original_length * min_distance; + } + + // Try to find a node that is closer than the selected active + // object (if it exists). + + voxalgo::VoxelLineIterator iterator(shootline.start / BS, + shootline.getVector() / BS); + v3s16 oldnode = iterator.m_current_node_pos; + // Indicates that a node was found. + bool is_node_found = false; + // If a node is found, it is possible that there's a node + // behind it with a large nodebox, so continue the search. + u16 node_foundcounter = 0; + // If a node is found, this is the center of the + // first nodebox the shootline meets. + v3f found_boxcenter(0, 0, 0); + // The untested nodes are in this range. + core::aabbox3d new_nodes; + while (true) { + // Test the nodes around the current node in search_range. + new_nodes = search_range; + new_nodes.MinEdge += iterator.m_current_node_pos; + new_nodes.MaxEdge += iterator.m_current_node_pos; + + // Only check new nodes + v3s16 delta = iterator.m_current_node_pos - oldnode; + if (delta.X > 0) + new_nodes.MinEdge.X = new_nodes.MaxEdge.X; + else if (delta.X < 0) + new_nodes.MaxEdge.X = new_nodes.MinEdge.X; + else if (delta.Y > 0) + new_nodes.MinEdge.Y = new_nodes.MaxEdge.Y; + else if (delta.Y < 0) + new_nodes.MaxEdge.Y = new_nodes.MinEdge.Y; + else if (delta.Z > 0) + new_nodes.MinEdge.Z = new_nodes.MaxEdge.Z; + else if (delta.Z < 0) + new_nodes.MaxEdge.Z = new_nodes.MinEdge.Z; + + // For each untested node + for (s16 x = new_nodes.MinEdge.X; x <= new_nodes.MaxEdge.X; x++) { + for (s16 y = new_nodes.MinEdge.Y; y <= new_nodes.MaxEdge.Y; y++) { + for (s16 z = new_nodes.MinEdge.Z; z <= new_nodes.MaxEdge.Z; z++) { + MapNode n; + v3s16 np(x, y, z); + bool is_valid_position; + + n = m_map->getNodeNoEx(np, &is_valid_position); + if (!(is_valid_position && + isPointableNode(n, nodedef, liquids_pointable))) { + continue; + } + std::vector boxes; + n.getSelectionBoxes(nodedef, &boxes, + n.getNeighbors(np, m_map)); + + v3f npf = intToFloat(np, BS); + for (std::vector::const_iterator i = boxes.begin(); + i != boxes.end(); ++i) { + aabb3f box = *i; + box.MinEdge += npf; + box.MaxEdge += npf; + v3f intersection_point; + v3s16 intersection_normal; + if (!boxLineCollision(box, shootline.start, shootline.getVector(), + &intersection_point, &intersection_normal)) { + continue; + } + f32 distance = (intersection_point - shootline.start).getLength(); + if (distance >= min_distance) { + continue; + } + result.type = POINTEDTHING_NODE; + result.node_undersurface = np; + result.intersection_point = intersection_point; + result.intersection_normal = intersection_normal; + found_boxcenter = box.getCenter(); + min_distance = distance; + is_node_found = true; + } + } + } + } + if (is_node_found) { + node_foundcounter++; + if (node_foundcounter > maximal_overcheck) { + break; + } + } + // Next node + if (iterator.hasNext()) { + oldnode = iterator.m_current_node_pos; + iterator.next(); + } else { + break; + } + } + + if (is_node_found) { + // Set undersurface and abovesurface nodes + f32 d = 0.002 * BS; + v3f fake_intersection = result.intersection_point; + // Move intersection towards its source block. + if (fake_intersection.X < found_boxcenter.X) + fake_intersection.X += d; + else + fake_intersection.X -= d; + + if (fake_intersection.Y < found_boxcenter.Y) + fake_intersection.Y += d; + else + fake_intersection.Y -= d; + + if (fake_intersection.Z < found_boxcenter.Z) + fake_intersection.Z += d; + else + fake_intersection.Z -= d; + + result.node_real_undersurface = floatToInt(fake_intersection, BS); + result.node_abovesurface = result.node_real_undersurface + + result.intersection_normal; + } + return result; +} -- cgit v1.2.3 From 8e7449e09253e138716d8dbad6a2ab5c6e089e28 Mon Sep 17 00:00:00 2001 From: Ner'zhul Date: Mon, 9 Jan 2017 20:39:22 +0100 Subject: Environment & IGameDef code refactoring (#4985) * Environment code refactoring * Cleanup includes & class declarations in client & server environment to improve build speed * ServerEnvironment::m_gamedef is now a pointer to Server instead of IGameDef, permitting to cleanup many casts. * Cleanup IGameDef * Move ITextureSource* IGameDef::getTextureSource() to Client only. * Also move ITextureSource *IGameDef::tsrc() helper * drop getShaderSource, getSceneManager, getSoundManager & getCamera abstract call * drop unused emerge() call * cleanup server unused functions (mentionned before) * Drop one unused parameter from ContentFeatures::updateTextures * move checkLocalPrivilege to Client * Remove some unnecessary casts * create_formspec_menu: remove IWritableTextureSource pointer, as client already knows it * Fix some comments * Change required IGameDef to Server/Client pointers * Previous change that game.cpp sometimes calls functions with Client + InventoryManager + IGameDef in same functions but it's the same objects * Remove duplicate Client pointer in GUIFormSpecMenu::GUIFormSpecMenu * drop ClientMap::sectorWasDrawn which is unused --- build/android/jni/Android.mk | 2 ++ src/camera.cpp | 21 ++++++------ src/camera.h | 6 ++-- src/client.cpp | 2 +- src/client.h | 17 +++++----- src/clientenvironment.cpp | 30 +++++++++--------- src/clientenvironment.h | 8 ++--- src/clientmap.cpp | 7 ++-- src/clientmap.h | 21 ++++-------- src/clientobject.cpp | 9 +++--- src/clientobject.h | 21 +++--------- src/collision.cpp | 3 ++ src/content_cao.cpp | 67 +++++++++++++++++++-------------------- src/content_cao.h | 9 +++--- src/content_cso.cpp | 13 +------- src/content_mapblock.cpp | 6 ++-- src/content_sao.cpp | 20 ++++++------ src/emerge.cpp | 14 ++++---- src/emerge.h | 3 +- src/environment.h | 7 ---- src/game.cpp | 59 +++++++++++++++------------------- src/gamedef.h | 29 ++--------------- src/guiEngine.cpp | 2 -- src/guiFormSpecMenu.cpp | 39 +++++++++++------------ src/guiFormSpecMenu.h | 6 +--- src/hud.cpp | 20 ++++++------ src/hud.h | 8 ++--- src/itemdef.cpp | 22 ++++++------- src/itemdef.h | 9 +++--- src/localplayer.cpp | 23 +++++++------- src/localplayer.h | 4 +-- src/mapblock_mesh.cpp | 25 +++++++-------- src/mapblock_mesh.h | 8 ++--- src/mg_biome.cpp | 11 +++---- src/mg_biome.h | 5 +-- src/mg_schematic.cpp | 10 +++--- src/mg_schematic.h | 6 ++-- src/nodedef.cpp | 16 ++++++---- src/nodedef.h | 4 +-- src/particles.cpp | 16 ++++------ src/particles.h | 2 +- src/pathfinder.cpp | 6 +--- src/script/lua_api/l_nodemeta.cpp | 6 +--- src/server.cpp | 18 ----------- src/server.h | 6 +--- src/serverenvironment.cpp | 20 ++++++------ src/serverenvironment.h | 12 ++++--- src/wieldmesh.cpp | 26 +++++++-------- src/wieldmesh.h | 6 ++-- 49 files changed, 301 insertions(+), 409 deletions(-) (limited to 'src/clientenvironment.cpp') diff --git a/build/android/jni/Android.mk b/build/android/jni/Android.mk index 2567457b9..3be856770 100644 --- a/build/android/jni/Android.mk +++ b/build/android/jni/Android.mk @@ -117,6 +117,7 @@ LOCAL_SRC_FILES := \ jni/src/cavegen.cpp \ jni/src/chat.cpp \ jni/src/client.cpp \ + jni/src/clientenvironment.cpp \ jni/src/clientiface.cpp \ jni/src/clientmap.cpp \ jni/src/clientmedia.cpp \ @@ -210,6 +211,7 @@ LOCAL_SRC_FILES := \ jni/src/rollback_interface.cpp \ jni/src/serialization.cpp \ jni/src/server.cpp \ + jni/src/serverenvironment.cpp \ jni/src/serverlist.cpp \ jni/src/serverobject.cpp \ jni/src/shader.cpp \ diff --git a/src/camera.cpp b/src/camera.cpp index 43980db1c..4768e8778 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -27,7 +27,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "settings.h" #include "wieldmesh.h" #include "noise.h" // easeCurve -#include "gamedef.h" #include "sound.h" #include "event.h" #include "profiler.h" @@ -41,7 +40,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "nodedef.h" Camera::Camera(scene::ISceneManager* smgr, MapDrawControl& draw_control, - IGameDef *gamedef): + Client *client): m_playernode(NULL), m_headnode(NULL), m_cameranode(NULL), @@ -50,7 +49,7 @@ Camera::Camera(scene::ISceneManager* smgr, MapDrawControl& draw_control, m_wieldnode(NULL), m_draw_control(draw_control), - m_gamedef(gamedef), + m_client(client), m_camera_position(0,0,0), m_camera_direction(0,0,0), @@ -88,7 +87,7 @@ Camera::Camera(scene::ISceneManager* smgr, MapDrawControl& draw_control, m_wieldmgr = smgr->createNewSceneManager(); m_wieldmgr->addCameraSceneNode(); m_wieldnode = new WieldMeshSceneNode(m_wieldmgr->getRootSceneNode(), m_wieldmgr, -1, false); - m_wieldnode->setItem(ItemStack(), m_gamedef); + m_wieldnode->setItem(ItemStack(), m_client); m_wieldnode->drop(); // m_wieldmgr grabbed it /* TODO: Add a callback function so these can be updated when a setting @@ -151,7 +150,7 @@ void Camera::step(f32 dtime) m_wield_change_timer = MYMIN(m_wield_change_timer + dtime, 0.125); if (m_wield_change_timer >= 0 && was_under_zero) - m_wieldnode->setItem(m_wield_item_next, m_gamedef); + m_wieldnode->setItem(m_wield_item_next, m_client); if (m_view_bobbing_state != 0) { @@ -189,7 +188,7 @@ void Camera::step(f32 dtime) (was > 0.5f && m_view_bobbing_anim <= 0.5f)); if(step) { MtEvent *e = new SimpleTriggerEvent("ViewBobbingStep"); - m_gamedef->event()->put(e); + m_client->event()->put(e); } } } @@ -210,10 +209,10 @@ void Camera::step(f32 dtime) if(m_digging_button == 0) { MtEvent *e = new SimpleTriggerEvent("CameraPunchLeft"); - m_gamedef->event()->put(e); + m_client->event()->put(e); } else if(m_digging_button == 1) { MtEvent *e = new SimpleTriggerEvent("CameraPunchRight"); - m_gamedef->event()->put(e); + m_client->event()->put(e); } } } @@ -352,7 +351,7 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime, my_cp.Y = m_camera_position.Y + (m_camera_direction.Y*-i); // Prevent camera positioned inside nodes - INodeDefManager *nodemgr = m_gamedef->ndef(); + INodeDefManager *nodemgr = m_client->ndef(); MapNode n = c_env.getClientMap().getNodeNoEx(floatToInt(my_cp, BS)); const ContentFeatures& features = nodemgr->get(n); if(features.walkable) @@ -390,7 +389,7 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime, // Get FOV f32 fov_degrees; - if (player->getPlayerControl().zoom && m_gamedef->checkLocalPrivilege("zoom")) { + if (player->getPlayerControl().zoom && m_client->checkLocalPrivilege("zoom")) { fov_degrees = m_cache_zoom_fov; } else { fov_degrees = m_cache_fov; @@ -468,7 +467,7 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime, const bool climbing = movement_Y && player->is_climbing; if ((walking || swimming || climbing) && m_cache_view_bobbing && - (!g_settings->getBool("free_move") || !m_gamedef->checkLocalPrivilege("fly"))) + (!g_settings->getBool("free_move") || !m_client->checkLocalPrivilege("fly"))) { // Start animation m_view_bobbing_state = 1; diff --git a/src/camera.h b/src/camera.h index cb0e9686d..b5be26718 100644 --- a/src/camera.h +++ b/src/camera.h @@ -33,7 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc., class LocalPlayer; struct MapDrawControl; -class IGameDef; +class Client; class WieldMeshSceneNode; struct Nametag { @@ -61,7 +61,7 @@ class Camera { public: Camera(scene::ISceneManager* smgr, MapDrawControl& draw_control, - IGameDef *gamedef); + Client *client); ~Camera(); // Get player scene node. @@ -189,7 +189,7 @@ private: // draw control MapDrawControl& m_draw_control; - IGameDef *m_gamedef; + Client *m_client; video::IVideoDriver *m_driver; // Absolute camera position diff --git a/src/client.cpp b/src/client.cpp index 693a90604..c2471dbd7 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -221,7 +221,7 @@ Client::Client( m_event(event), m_mesh_update_thread(), m_env( - new ClientMap(this, this, control, + new ClientMap(this, control, device->getSceneManager()->getRootSceneNode(), device->getSceneManager(), 666), device->getSceneManager(), diff --git a/src/client.h b/src/client.h index 9a09704a6..df3e7e605 100644 --- a/src/client.h +++ b/src/client.h @@ -34,7 +34,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "localplayer.h" #include "hud.h" #include "particles.h" -#include "network/networkpacket.h" struct MeshMakeData; class MapBlockMesh; @@ -51,6 +50,7 @@ class Database; class Mapper; struct MinimapMapblock; class Camera; +class NetworkPacket; struct QueuedMeshUpdate { @@ -402,9 +402,6 @@ public: void ProcessData(NetworkPacket *pkt); - // Returns true if something was received - bool AsyncProcessPacket(); - bool AsyncProcessData(); void Send(NetworkPacket* pkt); void interact(u8 action, const PointedThing& pointed); @@ -422,8 +419,9 @@ public: void sendRespawn(); void sendReady(); - ClientEnvironment& getEnv() - { return m_env; } + ClientEnvironment& getEnv() { return m_env; } + ITextureSource *tsrc() { return getTextureSource(); } + ISoundManager *sound() { return getSoundManager(); } // Causes urgent mesh updates (unlike Map::add/removeNodeWithEvent) void removeNode(v3s16 p); @@ -521,14 +519,15 @@ public: virtual IItemDefManager* getItemDefManager(); virtual INodeDefManager* getNodeDefManager(); virtual ICraftDefManager* getCraftDefManager(); - virtual ITextureSource* getTextureSource(); + ITextureSource* getTextureSource(); virtual IShaderSource* getShaderSource(); - virtual scene::ISceneManager* getSceneManager(); + IShaderSource *shsrc() { return getShaderSource(); } + scene::ISceneManager* getSceneManager(); virtual u16 allocateUnknownNodeId(const std::string &name); virtual ISoundManager* getSoundManager(); virtual MtEventManager* getEventManager(); virtual ParticleManager* getParticleManager(); - virtual bool checkLocalPrivilege(const std::string &priv) + bool checkLocalPrivilege(const std::string &priv) { return checkPrivilege(priv); } virtual scene::IAnimatedMesh* getMesh(const std::string &filename); diff --git a/src/clientenvironment.cpp b/src/clientenvironment.cpp index e831de109..65646c6b4 100644 --- a/src/clientenvironment.cpp +++ b/src/clientenvironment.cpp @@ -34,13 +34,13 @@ with this program; if not, write to the Free Software Foundation, Inc., */ ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr, - ITextureSource *texturesource, IGameDef *gamedef, + ITextureSource *texturesource, Client *client, IrrlichtDevice *irr): m_map(map), m_local_player(NULL), m_smgr(smgr), m_texturesource(texturesource), - m_gamedef(gamedef), + m_client(client), m_irr(irr) { char zero = 0; @@ -94,7 +94,7 @@ void ClientEnvironment::step(float dtime) stepTimeOfDay(dtime); // Get some settings - bool fly_allowed = m_gamedef->checkLocalPrivilege("fly"); + bool fly_allowed = m_client->checkLocalPrivilege("fly"); bool free_move = fly_allowed && g_settings->getBool("free_move"); // Get local player @@ -223,7 +223,7 @@ void ClientEnvironment::step(float dtime) f32 post_factor = 1; // 1 hp per node/s if(info.type == COLLISION_NODE) { - const ContentFeatures &f = m_gamedef->ndef()-> + 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"); @@ -237,7 +237,7 @@ void ClientEnvironment::step(float dtime) if(damage != 0){ damageLocalPlayer(damage, true); MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage"); - m_gamedef->event()->put(e); + m_client->event()->put(e); } } } @@ -259,11 +259,11 @@ void ClientEnvironment::step(float dtime) u32 damage_per_second = 0; damage_per_second = MYMAX(damage_per_second, - m_gamedef->ndef()->get(n1).damage_per_second); + m_client->ndef()->get(n1).damage_per_second); damage_per_second = MYMAX(damage_per_second, - m_gamedef->ndef()->get(n2).damage_per_second); + m_client->ndef()->get(n2).damage_per_second); damage_per_second = MYMAX(damage_per_second, - m_gamedef->ndef()->get(n3).damage_per_second); + m_client->ndef()->get(n3).damage_per_second); if(damage_per_second != 0) { @@ -272,7 +272,7 @@ void ClientEnvironment::step(float dtime) } // Protocol v29 make this behaviour obsolete - if (((Client*) getGameDef())->getProtoVersion() < 29) { + if (getGameDef()->getProtoVersion() < 29) { /* Drowning */ @@ -282,7 +282,7 @@ void ClientEnvironment::step(float dtime) // head v3s16 p = floatToInt(pf + v3f(0, BS * 1.6, 0), BS); MapNode n = m_map->getNodeNoEx(p); - ContentFeatures c = m_gamedef->ndef()->get(n); + ContentFeatures c = m_client->ndef()->get(n); u8 drowning_damage = c.drowning; if (drowning_damage > 0 && lplayer->hp > 0) { u16 breath = lplayer->getBreath(); @@ -306,7 +306,7 @@ void ClientEnvironment::step(float dtime) // head v3s16 p = floatToInt(pf + v3f(0, BS * 1.6, 0), BS); MapNode n = m_map->getNodeNoEx(p); - ContentFeatures c = m_gamedef->ndef()->get(n); + ContentFeatures c = m_client->ndef()->get(n); if (!lplayer->hp) { lplayer->setBreath(11); } else if (c.drowning == 0) { @@ -332,7 +332,7 @@ void ClientEnvironment::step(float dtime) v3s16 p = lplayer->getLightPosition(); node_at_lplayer = m_map->getNodeNoEx(p); - u16 light = getInteriorLight(node_at_lplayer, 0, m_gamedef->ndef()); + u16 light = getInteriorLight(node_at_lplayer, 0, m_client->ndef()); u8 day = light & 0xff; u8 night = (light >> 8) & 0xff; finalColorBlend(lplayer->light_color, day, night, day_night_ratio); @@ -360,7 +360,7 @@ void ClientEnvironment::step(float dtime) v3s16 p = obj->getLightPosition(); MapNode n = m_map->getNodeNoEx(p, &pos_ok); if (pos_ok) - light = n.getLightBlend(day_night_ratio, m_gamedef->ndef()); + light = n.getLightBlend(day_night_ratio, m_client->ndef()); else light = blend_light(day_night_ratio, LIGHT_SUN, 0); @@ -467,7 +467,7 @@ u16 ClientEnvironment::addActiveObject(ClientActiveObject *object) v3s16 p = object->getLightPosition(); MapNode n = m_map->getNodeNoEx(p, &pos_ok); if (pos_ok) - light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef()); + light = n.getLightBlend(getDayNightRatio(), m_client->ndef()); else light = blend_light(getDayNightRatio(), LIGHT_SUN, 0); @@ -480,7 +480,7 @@ void ClientEnvironment::addActiveObject(u16 id, u8 type, const std::string &init_data) { ClientActiveObject* obj = - ClientActiveObject::create((ActiveObjectType) type, m_gamedef, this); + ClientActiveObject::create((ActiveObjectType) type, m_client, this); if(obj == NULL) { infostream<<"ClientEnvironment::addActiveObject(): " diff --git a/src/clientenvironment.h b/src/clientenvironment.h index e6292b5b7..b30a7a6d7 100644 --- a/src/clientenvironment.h +++ b/src/clientenvironment.h @@ -30,6 +30,7 @@ class ClientMap; class ClientActiveObject; class GenericCAO; class LocalPlayer; +struct PointedThing; /* The client-side environment. @@ -66,15 +67,14 @@ class ClientEnvironment : public Environment { public: ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr, - ITextureSource *texturesource, IGameDef *gamedef, + ITextureSource *texturesource, Client *client, IrrlichtDevice *device); ~ClientEnvironment(); Map & getMap(); ClientMap & getClientMap(); - IGameDef *getGameDef() - { return m_gamedef; } + Client *getGameDef() { return m_client; } void step(f32 dtime); @@ -175,7 +175,7 @@ private: LocalPlayer *m_local_player; scene::ISceneManager *m_smgr; ITextureSource *m_texturesource; - IGameDef *m_gamedef; + Client *m_client; IrrlichtDevice *m_irr; UNORDERED_MAP m_active_objects; std::vector m_simple_objects; diff --git a/src/clientmap.cpp b/src/clientmap.cpp index 542eb03e8..7e688daad 100644 --- a/src/clientmap.cpp +++ b/src/clientmap.cpp @@ -35,13 +35,12 @@ with this program; if not, write to the Free Software Foundation, Inc., ClientMap::ClientMap( Client *client, - IGameDef *gamedef, MapDrawControl &control, scene::ISceneNode* parent, scene::ISceneManager* mgr, s32 id ): - Map(dout_client, gamedef), + Map(dout_client, client), scene::ISceneNode(parent, mgr, id), m_client(client), m_control(control), @@ -140,7 +139,7 @@ static bool isOccluded(Map *map, v3s16 p0, v3s16 p1, float step, float stepfac, return false; } -void ClientMap::getBlocksInViewRange(v3s16 cam_pos_nodes, +void ClientMap::getBlocksInViewRange(v3s16 cam_pos_nodes, v3s16 *p_blocks_min, v3s16 *p_blocks_max) { v3s16 box_nodes_d = m_control.wanted_range * v3s16(1, 1, 1); @@ -766,7 +765,7 @@ void ClientMap::renderPostFx(CameraMode cam_mode) const ContentFeatures& features = m_nodedef->get(n); video::SColor post_effect_color = features.post_effect_color; if(features.solidness == 2 && !(g_settings->getBool("noclip") && - m_gamedef->checkLocalPrivilege("noclip")) && + m_client->checkLocalPrivilege("noclip")) && cam_mode == CAMERA_MODE_FIRST) { post_effect_color = video::SColor(255, 0, 0, 0); diff --git a/src/clientmap.h b/src/clientmap.h index cb686ff33..84228f4ca 100644 --- a/src/clientmap.h +++ b/src/clientmap.h @@ -59,7 +59,7 @@ class ITextureSource; /* ClientMap - + This is the only map class that is able to render itself on screen. */ @@ -68,7 +68,6 @@ class ClientMap : public Map, public scene::ISceneNode public: ClientMap( Client *client, - IGameDef *gamedef, MapDrawControl &control, scene::ISceneNode* parent, scene::ISceneManager* mgr, @@ -114,13 +113,13 @@ public: driver->setTransform(video::ETS_WORLD, AbsoluteTransformation); renderMap(driver, SceneManager->getSceneNodeRenderPass()); } - + virtual const aabb3f &getBoundingBox() const { return m_box; } - - void getBlocksInViewRange(v3s16 cam_pos_nodes, + + void getBlocksInViewRange(v3s16 cam_pos_nodes, v3s16 *p_blocks_min, v3s16 *p_blocks_max); void updateDrawList(video::IVideoDriver* driver); void renderMap(video::IVideoDriver* driver, s32 pass); @@ -132,20 +131,14 @@ public: // For debug printing virtual void PrintInfo(std::ostream &out); - - // Check if sector was drawn on last render() - bool sectorWasDrawn(v2s16 p) - { - return (m_last_drawn_sectors.find(p) != m_last_drawn_sectors.end()); - } const MapDrawControl & getControl() const { return m_control; } f32 getCameraFov() const { return m_camera_fov; } private: Client *m_client; - + aabb3f m_box; - + MapDrawControl &m_control; v3f m_camera_position; @@ -154,7 +147,7 @@ private: v3s16 m_camera_offset; std::map m_drawlist; - + std::set m_last_drawn_sectors; bool m_cache_trilinear_filter; diff --git a/src/clientobject.cpp b/src/clientobject.cpp index ff3f47187..89a0474a4 100644 --- a/src/clientobject.cpp +++ b/src/clientobject.cpp @@ -20,16 +20,15 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "clientobject.h" #include "debug.h" #include "porting.h" -#include "constants.h" /* ClientActiveObject */ -ClientActiveObject::ClientActiveObject(u16 id, IGameDef *gamedef, +ClientActiveObject::ClientActiveObject(u16 id, Client *client, ClientEnvironment *env): ActiveObject(id), - m_gamedef(gamedef), + m_client(client), m_env(env) { } @@ -40,7 +39,7 @@ ClientActiveObject::~ClientActiveObject() } ClientActiveObject* ClientActiveObject::create(ActiveObjectType type, - IGameDef *gamedef, ClientEnvironment *env) + Client *client, ClientEnvironment *env) { // Find factory function UNORDERED_MAP::iterator n = m_types.find(type); @@ -52,7 +51,7 @@ ClientActiveObject* ClientActiveObject::create(ActiveObjectType type, } Factory f = n->second; - ClientActiveObject *object = (*f)(gamedef, env); + ClientActiveObject *object = (*f)(client, env); return object; } diff --git a/src/clientobject.h b/src/clientobject.h index 83931e438..f0bde0adc 100644 --- a/src/clientobject.h +++ b/src/clientobject.h @@ -25,20 +25,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "util/cpp11_container.h" -/* - -Some planning -------------- - -* Client receives a network packet with information of added objects - in it -* Client supplies the information to its ClientEnvironment -* The environment adds the specified objects to itself - -*/ - class ClientEnvironment; class ITextureSource; +class Client; class IGameDef; class LocalPlayer; struct ItemStack; @@ -47,7 +36,7 @@ class WieldMeshSceneNode; class ClientActiveObject : public ActiveObject { public: - ClientActiveObject(u16 id, IGameDef *gamedef, ClientEnvironment *env); + ClientActiveObject(u16 id, Client *client, ClientEnvironment *env); virtual ~ClientActiveObject(); virtual void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc, @@ -89,7 +78,7 @@ public: virtual void initialize(const std::string &data){} // Create a certain type of ClientActiveObject - static ClientActiveObject* create(ActiveObjectType type, IGameDef *gamedef, + static ClientActiveObject* create(ActiveObjectType type, Client *client, ClientEnvironment *env); // If returns true, punch will not be sent to the server @@ -99,9 +88,9 @@ public: protected: // Used for creating objects based on type - typedef ClientActiveObject* (*Factory)(IGameDef *gamedef, ClientEnvironment *env); + typedef ClientActiveObject* (*Factory)(Client *client, ClientEnvironment *env); static void registerType(u16 type, Factory f); - IGameDef *m_gamedef; + Client *m_client; ClientEnvironment *m_env; private: // Used for creating objects based on type diff --git a/src/collision.cpp b/src/collision.cpp index 595fa8059..c0891c152 100644 --- a/src/collision.cpp +++ b/src/collision.cpp @@ -22,9 +22,12 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "map.h" #include "nodedef.h" #include "gamedef.h" +#ifndef SERVER #include "clientenvironment.h" +#endif #include "serverenvironment.h" #include "serverobject.h" +#include "util/timetaker.h" #include "profiler.h" // float error is 10 - 9.96875 = 0.03125 diff --git a/src/content_cao.cpp b/src/content_cao.cpp index a02d5168e..a4c0bf14d 100644 --- a/src/content_cao.cpp +++ b/src/content_cao.cpp @@ -33,7 +33,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "collision.h" #include "settings.h" #include "serialization.h" // For decompressZlib -#include "gamedef.h" #include "clientobject.h" #include "mesh.h" #include "itemdef.h" @@ -139,7 +138,7 @@ static void setBillboardTextureMatrix(scene::IBillboardSceneNode *bill, class TestCAO : public ClientActiveObject { public: - TestCAO(IGameDef *gamedef, ClientEnvironment *env); + TestCAO(Client *client, ClientEnvironment *env); virtual ~TestCAO(); ActiveObjectType getType() const @@ -147,7 +146,7 @@ public: return ACTIVEOBJECT_TYPE_TEST; } - static ClientActiveObject* create(IGameDef *gamedef, ClientEnvironment *env); + static ClientActiveObject* create(Client *client, ClientEnvironment *env); void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc, IrrlichtDevice *irr); @@ -169,8 +168,8 @@ private: // Prototype TestCAO proto_TestCAO(NULL, NULL); -TestCAO::TestCAO(IGameDef *gamedef, ClientEnvironment *env): - ClientActiveObject(0, gamedef, env), +TestCAO::TestCAO(Client *client, ClientEnvironment *env): + ClientActiveObject(0, client, env), m_node(NULL), m_position(v3f(0,10*BS,0)) { @@ -181,9 +180,9 @@ TestCAO::~TestCAO() { } -ClientActiveObject* TestCAO::create(IGameDef *gamedef, ClientEnvironment *env) +ClientActiveObject* TestCAO::create(Client *client, ClientEnvironment *env) { - return new TestCAO(gamedef, env); + return new TestCAO(client, env); } void TestCAO::addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc, @@ -283,7 +282,7 @@ void TestCAO::processMessage(const std::string &data) class ItemCAO : public ClientActiveObject { public: - ItemCAO(IGameDef *gamedef, ClientEnvironment *env); + ItemCAO(Client *client, ClientEnvironment *env); virtual ~ItemCAO(); ActiveObjectType getType() const @@ -291,7 +290,7 @@ public: return ACTIVEOBJECT_TYPE_ITEM; } - static ClientActiveObject* create(IGameDef *gamedef, ClientEnvironment *env); + static ClientActiveObject* create(Client *client, ClientEnvironment *env); void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc, IrrlichtDevice *irr); @@ -331,13 +330,13 @@ private: // Prototype ItemCAO proto_ItemCAO(NULL, NULL); -ItemCAO::ItemCAO(IGameDef *gamedef, ClientEnvironment *env): - ClientActiveObject(0, gamedef, env), +ItemCAO::ItemCAO(Client *client, ClientEnvironment *env): + ClientActiveObject(0, client, env), m_selection_box(-BS/3.,0.0,-BS/3., BS/3.,BS*2./3.,BS/3.), m_node(NULL), m_position(v3f(0,10*BS,0)) { - if(!gamedef && !env) + if(!client && !env) { ClientActiveObject::registerType(getType(), create); } @@ -347,9 +346,9 @@ ItemCAO::~ItemCAO() { } -ClientActiveObject* ItemCAO::create(IGameDef *gamedef, ClientEnvironment *env) +ClientActiveObject* ItemCAO::create(Client *client, ClientEnvironment *env) { - return new ItemCAO(gamedef, env); + return new ItemCAO(client, env); } void ItemCAO::addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc, @@ -433,7 +432,7 @@ void ItemCAO::updateNodePos() void ItemCAO::updateInfoText() { try{ - IItemDefManager *idef = m_gamedef->idef(); + IItemDefManager *idef = m_client->idef(); ItemStack item; item.deSerialize(m_itemstring, idef); if(item.isKnown(idef)) @@ -458,10 +457,10 @@ void ItemCAO::updateTexture() std::istringstream is(m_itemstring, std::ios_base::binary); video::ITexture *texture = NULL; try{ - IItemDefManager *idef = m_gamedef->idef(); + IItemDefManager *idef = m_client->idef(); ItemStack item; item.deSerialize(is, idef); - texture = idef->getInventoryTexture(item.getDefinition(idef).name, m_gamedef); + texture = idef->getInventoryTexture(item.getDefinition(idef).name, m_client); } catch(SerializationError &e) { @@ -538,15 +537,15 @@ void ItemCAO::initialize(const std::string &data) #include "genericobject.h" -GenericCAO::GenericCAO(IGameDef *gamedef, ClientEnvironment *env): - ClientActiveObject(0, gamedef, env), +GenericCAO::GenericCAO(Client *client, ClientEnvironment *env): + ClientActiveObject(0, client, env), // m_is_player(false), m_is_local_player(false), // m_smgr(NULL), m_irr(NULL), - m_gamedef(NULL), + m_client(NULL), m_selection_box(-BS/3.,-BS/3.,-BS/3., BS/3.,BS/3.,BS/3.), m_meshnode(NULL), m_animated_meshnode(NULL), @@ -581,10 +580,10 @@ GenericCAO::GenericCAO(IGameDef *gamedef, ClientEnvironment *env): m_last_light(255), m_is_visible(false) { - if (gamedef == NULL) { + if (client == NULL) { ClientActiveObject::registerType(getType(), create); } else { - m_gamedef = gamedef; + m_client = client; } } @@ -793,7 +792,7 @@ void GenericCAO::removeFromScene(bool permanent) } if (m_nametag) { - m_gamedef->getCamera()->removeNametag(m_nametag); + m_client->getCamera()->removeNametag(m_nametag); m_nametag = NULL; } } @@ -906,7 +905,7 @@ void GenericCAO::addToScene(scene::ISceneManager *smgr, } else if(m_prop.visual == "mesh") { infostream<<"GenericCAO::addToScene(): mesh"<getMesh(m_prop.mesh); + scene::IAnimatedMesh *mesh = m_client->getMesh(m_prop.mesh); if(mesh) { m_animated_meshnode = smgr->addAnimatedMeshSceneNode(mesh, NULL); @@ -937,12 +936,12 @@ void GenericCAO::addToScene(scene::ISceneManager *smgr, infostream<<"textures: "<= 1){ infostream<<"textures[0]: "<idef(); + IItemDefManager *idef = m_client->idef(); ItemStack item(m_prop.textures[0], 1, 0, "", idef); m_wield_meshnode = new WieldMeshSceneNode( smgr->getRootSceneNode(), smgr, -1); - m_wield_meshnode->setItem(item, m_gamedef); + m_wield_meshnode->setItem(item, m_client); m_wield_meshnode->setScale(v3f(m_prop.visual_size.X/2, m_prop.visual_size.Y/2, @@ -959,7 +958,7 @@ void GenericCAO::addToScene(scene::ISceneManager *smgr, scene::ISceneNode *node = getSceneNode(); if (node && m_prop.nametag != "" && !m_is_local_player) { // Add nametag - m_nametag = m_gamedef->getCamera()->addNametag(node, + m_nametag = m_client->getCamera()->addNametag(node, m_prop.nametag, m_prop.nametag_color); } @@ -1058,11 +1057,11 @@ void GenericCAO::step(float dtime, ClientEnvironment *env) // increase speed if using fast or flying fast if((g_settings->getBool("fast_move") && - m_gamedef->checkLocalPrivilege("fast")) && + m_client->checkLocalPrivilege("fast")) && (controls.aux1 || (!player->touching_ground && g_settings->getBool("free_move") && - m_gamedef->checkLocalPrivilege("fly")))) + m_client->checkLocalPrivilege("fly")))) new_speed *= 1.5; // slowdown speed if sneeking if(controls.sneak && walking) @@ -1129,7 +1128,7 @@ void GenericCAO::step(float dtime, ClientEnvironment *env) } removeFromScene(false); - addToScene(m_smgr, m_gamedef->tsrc(), m_irr); + addToScene(m_smgr, m_client->tsrc(), m_irr); // Attachments, part 2: Now that the parent has been refreshed, put its attachments back for (std::vector::size_type i = 0; i < m_children.size(); i++) { @@ -1199,12 +1198,12 @@ void GenericCAO::step(float dtime, ClientEnvironment *env) m_step_distance_counter = 0; if(!m_is_local_player && m_prop.makes_footstep_sound) { - INodeDefManager *ndef = m_gamedef->ndef(); + INodeDefManager *ndef = m_client->ndef(); v3s16 p = floatToInt(getPosition() + v3f(0, (m_prop.collisionbox.MinEdge.Y-0.5)*BS, 0), BS); MapNode n = m_env->getMap().getNodeNoEx(p); SimpleSoundSpec spec = ndef->get(n).sound_footstep; - m_gamedef->sound()->playSoundAt(spec, false, getPosition()); + m_client->sound()->playSoundAt(spec, false, getPosition()); } } } @@ -1305,7 +1304,7 @@ void GenericCAO::updateTexturePos() void GenericCAO::updateTextures(const std::string &mod) { - ITextureSource *tsrc = m_gamedef->tsrc(); + ITextureSource *tsrc = m_client->tsrc(); bool use_trilinear_filter = g_settings->getBool("trilinear_filter"); bool use_bilinear_filter = g_settings->getBool("bilinear_filter"); @@ -1778,7 +1777,7 @@ bool GenericCAO::directReportPunch(v3f dir, const ItemStack *punchitem, { assert(punchitem); // pre-condition const ToolCapabilities *toolcap = - &punchitem->getToolCapabilities(m_gamedef->idef()); + &punchitem->getToolCapabilities(m_client->idef()); PunchDamageResult result = getPunchDamage( m_armor_groups, toolcap, diff --git a/src/content_cao.h b/src/content_cao.h index a158e8296..96a160055 100644 --- a/src/content_cao.h +++ b/src/content_cao.h @@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "itemgroup.h" class Camera; +class Client; struct Nametag; /* @@ -68,7 +69,7 @@ private: // scene::ISceneManager *m_smgr; IrrlichtDevice *m_irr; - IGameDef *m_gamedef; + Client *m_client; aabb3f m_selection_box; scene::IMeshSceneNode *m_meshnode; scene::IAnimatedMeshSceneNode *m_animated_meshnode; @@ -109,13 +110,13 @@ private: std::vector m_children; public: - GenericCAO(IGameDef *gamedef, ClientEnvironment *env); + GenericCAO(Client *client, ClientEnvironment *env); ~GenericCAO(); - static ClientActiveObject* create(IGameDef *gamedef, ClientEnvironment *env) + static ClientActiveObject* create(Client *client, ClientEnvironment *env) { - return new GenericCAO(gamedef, env); + return new GenericCAO(client, env); } inline ActiveObjectType getType() const diff --git a/src/content_cso.cpp b/src/content_cso.cpp index c0407f460..aca71212b 100644 --- a/src/content_cso.cpp +++ b/src/content_cso.cpp @@ -21,20 +21,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "client/tile.h" #include "clientenvironment.h" -#include "gamedef.h" +#include "client.h" #include "map.h" -/* -static void setBillboardTextureMatrix(scene::IBillboardSceneNode *bill, - float txs, float tys, int col, int row) -{ - video::SMaterial& material = bill->getMaterial(0); - core::matrix4& matrix = material.getTextureMatrix(0); - matrix.setTextureTranslate(txs*col, tys*row); - matrix.setTextureScale(txs, tys); -} -*/ - class SmokePuffCSO: public ClientSimpleObject { float m_age; diff --git a/src/content_mapblock.cpp b/src/content_mapblock.cpp index 8ce0f1e0a..a7134590b 100644 --- a/src/content_mapblock.cpp +++ b/src/content_mapblock.cpp @@ -26,7 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "client/tile.h" #include "mesh.h" #include -#include "gamedef.h" +#include "client.h" #include "log.h" #include "noise.h" @@ -188,8 +188,8 @@ static inline int NeighborToIndex(const v3s16 &pos) void mapblock_mesh_generate_special(MeshMakeData *data, MeshCollector &collector) { - INodeDefManager *nodedef = data->m_gamedef->ndef(); - scene::ISceneManager* smgr = data->m_gamedef->getSceneManager(); + INodeDefManager *nodedef = data->m_client->ndef(); + scene::ISceneManager* smgr = data->m_client->getSceneManager(); scene::IMeshManipulator* meshmanip = smgr->getMeshManipulator(); // 0ms diff --git a/src/content_sao.cpp b/src/content_sao.cpp index f866d4372..dd8bdc592 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -273,7 +273,7 @@ void LuaEntitySAO::step(float dtime, bool send_recommended) v3f p_pos = m_base_position; v3f p_velocity = m_velocity; v3f p_acceleration = m_acceleration; - moveresult = collisionMoveSimple(m_env,m_env->getGameDef(), + moveresult = collisionMoveSimple(m_env, m_env->getGameDef(), pos_max_d, box, m_prop.stepheight, dtime, &p_pos, &p_velocity, p_acceleration, this, m_prop.collideWithObjects); @@ -945,7 +945,7 @@ void PlayerSAO::step(float dtime, bool send_recommended) // get head position v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS); MapNode n = m_env->getMap().getNodeNoEx(p); - const ContentFeatures &c = ((Server*) m_env->getGameDef())->ndef()->get(n); + const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n); // If node generates drown if (c.drowning > 0) { if (m_hp > 0 && m_breath > 0) @@ -954,7 +954,7 @@ void PlayerSAO::step(float dtime, bool send_recommended) // No more breath, damage player if (m_breath == 0) { setHP(m_hp - c.drowning); - ((Server*) m_env->getGameDef())->SendPlayerHPOrDie(this); + m_env->getGameDef()->SendPlayerHPOrDie(this); } } } @@ -963,7 +963,7 @@ void PlayerSAO::step(float dtime, bool send_recommended) // get head position v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS); MapNode n = m_env->getMap().getNodeNoEx(p); - const ContentFeatures &c = ((Server*) m_env->getGameDef())->ndef()->get(n); + const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n); // If player is alive & no drowning, breath if (m_hp > 0 && c.drowning == 0) setBreath(m_breath + 1); @@ -985,7 +985,7 @@ void PlayerSAO::step(float dtime, bool send_recommended) m_attachment_position = v3f(0,0,0); m_attachment_rotation = v3f(0,0,0); setBasePosition(m_last_good_position); - ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id); + m_env->getGameDef()->SendMovePlayer(m_peer_id); } //dstream<<"PlayerSAO::step: dtime: "<getGameDef())->SendMovePlayer(m_peer_id); + m_env->getGameDef()->SendMovePlayer(m_peer_id); } void PlayerSAO::moveTo(v3f pos, bool continuous) @@ -1118,7 +1118,7 @@ void PlayerSAO::moveTo(v3f pos, bool continuous) setBasePosition(pos); // Movement caused by this command is always valid m_last_good_position = pos; - ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id); + m_env->getGameDef()->SendMovePlayer(m_peer_id); } void PlayerSAO::setYaw(const float yaw) @@ -1148,7 +1148,7 @@ void PlayerSAO::setWantedRange(const s16 range) void PlayerSAO::setYawAndSend(const float yaw) { setYaw(yaw); - ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id); + m_env->getGameDef()->SendMovePlayer(m_peer_id); } void PlayerSAO::setPitch(const float pitch) @@ -1162,7 +1162,7 @@ void PlayerSAO::setPitch(const float pitch) void PlayerSAO::setPitchAndSend(const float pitch) { setPitch(pitch); - ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id); + m_env->getGameDef()->SendMovePlayer(m_peer_id); } int PlayerSAO::punch(v3f dir, @@ -1273,7 +1273,7 @@ void PlayerSAO::setBreath(const u16 breath, bool send) m_breath = MYMIN(breath, PLAYER_MAX_BREATH); if (send) - ((Server *) m_env->getGameDef())->SendPlayerBreath(this); + m_env->getGameDef()->SendPlayerBreath(this); } void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups) diff --git a/src/emerge.cpp b/src/emerge.cpp index 25b2e924b..3f0a46010 100644 --- a/src/emerge.cpp +++ b/src/emerge.cpp @@ -89,13 +89,13 @@ private: //// EmergeManager //// -EmergeManager::EmergeManager(IGameDef *gamedef) +EmergeManager::EmergeManager(Server *server) { - this->ndef = gamedef->getNodeDefManager(); - this->biomemgr = new BiomeManager(gamedef); - this->oremgr = new OreManager(gamedef); - this->decomgr = new DecorationManager(gamedef); - this->schemmgr = new SchematicManager(gamedef); + this->ndef = server->getNodeDefManager(); + this->biomemgr = new BiomeManager(server); + this->oremgr = new OreManager(server); + this->decomgr = new DecorationManager(server); + this->schemmgr = new SchematicManager(server); this->gen_notify_on = 0; // Note that accesses to this variable are not synchronized. @@ -128,7 +128,7 @@ EmergeManager::EmergeManager(IGameDef *gamedef) m_qlimit_generate = 1; for (s16 i = 0; i < nthreads; i++) - m_threads.push_back(new EmergeThread((Server *)gamedef, i)); + m_threads.push_back(new EmergeThread(server, i)); infostream << "EmergeManager: using " << nthreads << " threads" << std::endl; } diff --git a/src/emerge.h b/src/emerge.h index 71ad97da3..76653e6cd 100644 --- a/src/emerge.h +++ b/src/emerge.h @@ -42,6 +42,7 @@ class BiomeManager; class OreManager; class DecorationManager; class SchematicManager; +class Server; // Structure containing inputs/outputs for chunk generation struct BlockMakeData { @@ -115,7 +116,7 @@ public: SchematicManager *schemmgr; // Methods - EmergeManager(IGameDef *gamedef); + EmergeManager(Server *server); ~EmergeManager(); bool initMapgens(MapgenParams *mgparams); diff --git a/src/environment.h b/src/environment.h index 14a18421b..0cc3222f9 100644 --- a/src/environment.h +++ b/src/environment.h @@ -42,13 +42,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "threading/atomic.h" #include "network/networkprotocol.h" // for AccessDeniedCode -class ITextureSource; -class IGameDef; -class Map; -class GameScripting; -class Player; -class PointedThing; - class Environment { public: diff --git a/src/game.cpp b/src/game.cpp index cfa6234ff..1070cb1b2 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -916,16 +916,14 @@ bool nodePlacementPrediction(Client &client, } static inline void create_formspec_menu(GUIFormSpecMenu **cur_formspec, - InventoryManager *invmgr, IGameDef *gamedef, - IWritableTextureSource *tsrc, IrrlichtDevice *device, - JoystickController *joystick, - IFormSource *fs_src, TextDest *txt_dest, Client *client) + Client *client, IrrlichtDevice *device, JoystickController *joystick, + IFormSource *fs_src, TextDest *txt_dest) { if (*cur_formspec == 0) { *cur_formspec = new GUIFormSpecMenu(device, joystick, - guiroot, -1, &g_menumgr, invmgr, gamedef, tsrc, - fs_src, txt_dest, client); + guiroot, -1, &g_menumgr, client, client->getTextureSource(), + fs_src, txt_dest); (*cur_formspec)->doPause = false; /* @@ -950,9 +948,9 @@ static inline void create_formspec_menu(GUIFormSpecMenu **cur_formspec, #endif static void show_deathscreen(GUIFormSpecMenu **cur_formspec, - InventoryManager *invmgr, IGameDef *gamedef, + Client *client, IWritableTextureSource *tsrc, IrrlichtDevice *device, - JoystickController *joystick, Client *client) + JoystickController *joystick) { std::string formspec = std::string(FORMSPEC_VERSION_STRING) + @@ -968,13 +966,12 @@ static void show_deathscreen(GUIFormSpecMenu **cur_formspec, FormspecFormSource *fs_src = new FormspecFormSource(formspec); LocalFormspecHandler *txt_dst = new LocalFormspecHandler("MT_DEATH_SCREEN", client); - create_formspec_menu(cur_formspec, invmgr, gamedef, tsrc, device, - joystick, fs_src, txt_dst, NULL); + create_formspec_menu(cur_formspec, client, device, joystick, fs_src, txt_dst); } /******************************************************************************/ static void show_pause_menu(GUIFormSpecMenu **cur_formspec, - InventoryManager *invmgr, IGameDef *gamedef, + Client *client, IWritableTextureSource *tsrc, IrrlichtDevice *device, JoystickController *joystick, bool singleplayermode) { @@ -1041,8 +1038,7 @@ static void show_pause_menu(GUIFormSpecMenu **cur_formspec, FormspecFormSource *fs_src = new FormspecFormSource(os.str()); LocalFormspecHandler *txt_dst = new LocalFormspecHandler("MT_PAUSE_MENU"); - create_formspec_menu(cur_formspec, invmgr, gamedef, tsrc, device, - joystick, fs_src, txt_dst, NULL); + create_formspec_menu(cur_formspec, client, device, joystick, fs_src, txt_dst); std::string con("btn_continue"); (*cur_formspec)->setFocus(con); (*cur_formspec)->doPause = true; @@ -1534,7 +1530,6 @@ private: bool *kill; std::string *error_message; bool *reconnect_requested; - IGameDef *gamedef; // Convenience (same as *client) scene::ISceneNode *skybox; bool random_input; @@ -2011,7 +2006,7 @@ bool Game::createClient(const std::string &playername, /* Camera */ - camera = new Camera(smgr, *draw_control, gamedef); + camera = new Camera(smgr, *draw_control, client); if (!camera || !camera->successfullyCreated(*error_message)) return false; client->setCamera(camera); @@ -2068,7 +2063,7 @@ bool Game::createClient(const std::string &playername, player->hurt_tilt_timer = 0; player->hurt_tilt_strength = 0; - hud = new Hud(driver, smgr, guienv, gamedef, player, local_inventory); + hud = new Hud(driver, smgr, guienv, client, player, local_inventory); if (!hud) { *error_message = "Memory error: could not create HUD"; @@ -2198,8 +2193,6 @@ bool Game::connectToServer(const std::string &playername, if (!client) return false; - gamedef = client; // Client acts as our GameDef - infostream << "Connecting to server at "; connect_address.print(&infostream); infostream << std::endl; @@ -2445,7 +2438,7 @@ inline bool Game::handleCallbacks() void Game::processQueues() { texture_src->processQueue(); - itemdef_manager->processQueue(gamedef); + itemdef_manager->processQueue(client); shader_src->processQueue(); } @@ -2617,7 +2610,7 @@ void Game::processKeyInput(VolatileRunFlags *flags, openInventory(); } else if (wasKeyDown(KeyType::ESC) || input->wasKeyDown(CancelKey)) { if (!gui_chat_console->isOpenInhibited()) { - show_pause_menu(¤t_formspec, client, gamedef, + show_pause_menu(¤t_formspec, client, texture_src, device, &input->joystick, simple_singleplayer_mode); } @@ -2769,8 +2762,7 @@ void Game::openInventory() PlayerInventoryFormSource *fs_src = new PlayerInventoryFormSource(client); TextDest *txt_dst = new TextDestPlayerInventory(client); - create_formspec_menu(¤t_formspec, client, gamedef, texture_src, - device, &input->joystick, fs_src, txt_dst, client); + create_formspec_menu(¤t_formspec, client, device, &input->joystick, fs_src, txt_dst); cur_formname = ""; InventoryLocation inventoryloc; @@ -3245,13 +3237,13 @@ void Game::processClientEvents(CameraOrientation *cam, float *damage_flash) rangelim(event.player_damage.amount / 4, 1.0, 4.0); MtEvent *e = new SimpleTriggerEvent("PlayerDamage"); - gamedef->event()->put(e); + client->event()->put(e); } else if (event.type == CE_PLAYER_FORCE_MOVE) { cam->camera_yaw = event.player_force_move.yaw; cam->camera_pitch = event.player_force_move.pitch; } else if (event.type == CE_DEATHSCREEN) { - show_deathscreen(¤t_formspec, client, gamedef, texture_src, - device, &input->joystick, client); + show_deathscreen(¤t_formspec, client, texture_src, + device, &input->joystick); chat_backend->addMessage(L"", L"You died."); @@ -3271,9 +3263,8 @@ void Game::processClientEvents(CameraOrientation *cam, float *damage_flash) TextDestPlayerInventory *txt_dst = new TextDestPlayerInventory(client, *(event.show_formspec.formname)); - create_formspec_menu(¤t_formspec, client, gamedef, - texture_src, device, &input->joystick, - fs_src, txt_dst, client); + create_formspec_menu(¤t_formspec, client, device, &input->joystick, + fs_src, txt_dst); cur_formname = *(event.show_formspec.formname); } @@ -3282,7 +3273,7 @@ void Game::processClientEvents(CameraOrientation *cam, float *damage_flash) } else if ((event.type == CE_SPAWN_PARTICLE) || (event.type == CE_ADD_PARTICLESPAWNER) || (event.type == CE_DELETE_PARTICLESPAWNER)) { - client->getParticleManager()->handleParticleEvent(&event, gamedef, + client->getParticleManager()->handleParticleEvent(&event, client, smgr, player); } else if (event.type == CE_HUDADD) { u32 id = event.hudadd.id; @@ -3840,8 +3831,8 @@ void Game::handlePointingAtNode(GameRunData *runData, &client->getEnv().getClientMap(), nodepos); TextDest *txt_dst = new TextDestNodeMetadata(nodepos, client); - create_formspec_menu(¤t_formspec, client, gamedef, - texture_src, device, &input->joystick, fs_src, txt_dst, client); + create_formspec_menu(¤t_formspec, client, + device, &input->joystick, fs_src, txt_dst); cur_formname = ""; current_formspec->setFormSpec(meta->getString("formspec"), inventoryloc); @@ -3972,7 +3963,7 @@ void Game::handleDigging(GameRunData *runData, if (m_cache_enable_particles) { const ContentFeatures &features = client->getNodeDefManager()->get(n); - client->getParticleManager()->addPunchingParticles(gamedef, smgr, + client->getParticleManager()->addPunchingParticles(client, smgr, player, nodepos, features.tiles); } } @@ -4019,7 +4010,7 @@ void Game::handleDigging(GameRunData *runData, if (m_cache_enable_particles) { const ContentFeatures &features = client->getNodeDefManager()->get(wasnode); - client->getParticleManager()->addDiggingParticles(gamedef, smgr, + client->getParticleManager()->addDiggingParticles(client, smgr, player, nodepos, features.tiles); } @@ -4043,7 +4034,7 @@ void Game::handleDigging(GameRunData *runData, // Send event to trigger sound MtEvent *e = new NodeDugEvent(nodepos, wasnode); - gamedef->event()->put(e); + client->event()->put(e); } if (runData->dig_time_complete < 100000.0) { diff --git a/src/gamedef.h b/src/gamedef.h index 7e3da4cac..cb624bd6a 100644 --- a/src/gamedef.h +++ b/src/gamedef.h @@ -53,47 +53,22 @@ public: virtual INodeDefManager* getNodeDefManager()=0; virtual ICraftDefManager* getCraftDefManager()=0; - // This is always thread-safe, but referencing the irrlicht texture - // pointers in other threads than main thread will make things explode. - virtual ITextureSource* getTextureSource()=0; - - virtual IShaderSource* getShaderSource()=0; - // Used for keeping track of names/ids of unknown nodes virtual u16 allocateUnknownNodeId(const std::string &name)=0; - // Only usable on the client - virtual ISoundManager* getSoundManager()=0; virtual MtEventManager* getEventManager()=0; - virtual scene::IAnimatedMesh* getMesh(const std::string &filename) - { return NULL; } - virtual scene::ISceneManager* getSceneManager()=0; - - virtual Camera* getCamera() - { return NULL; } - virtual void setCamera(Camera *camera) {} // Only usable on the server, and NOT thread-safe. It is usable from the // environment thread. - virtual IRollbackManager* getRollbackManager(){return NULL;} - - // Only usable on the server. Thread safe if not written while running threads. - virtual EmergeManager *getEmergeManager() { return NULL; } - - // Used on the client - virtual bool checkLocalPrivilege(const std::string &priv) - { return false; } + virtual IRollbackManager* getRollbackManager() { return NULL; } // Shorthands IItemDefManager *idef() { return getItemDefManager(); } INodeDefManager *ndef() { return getNodeDefManager(); } ICraftDefManager *cdef() { return getCraftDefManager(); } - ITextureSource *tsrc() { return getTextureSource(); } - ISoundManager *sound() { return getSoundManager(); } - IShaderSource *shsrc() { return getShaderSource(); } + MtEventManager *event() { return getEventManager(); } IRollbackManager *rollback() { return getRollbackManager();} - EmergeManager *emerge() { return getEmergeManager(); } }; #endif diff --git a/src/guiEngine.cpp b/src/guiEngine.cpp index a3c35f68d..6d66ed08d 100644 --- a/src/guiEngine.cpp +++ b/src/guiEngine.cpp @@ -194,11 +194,9 @@ GUIEngine::GUIEngine( irr::IrrlichtDevice* dev, -1, m_menumanager, NULL /* &client */, - NULL /* gamedef */, m_texture_source, m_formspecgui, m_buttonhandler, - NULL, false); m_menu->allowClose(false); diff --git a/src/guiFormSpecMenu.cpp b/src/guiFormSpecMenu.cpp index bfc7a9b79..45b0e9c11 100644 --- a/src/guiFormSpecMenu.cpp +++ b/src/guiFormSpecMenu.cpp @@ -81,13 +81,12 @@ static unsigned int font_line_height(gui::IGUIFont *font) GUIFormSpecMenu::GUIFormSpecMenu(irr::IrrlichtDevice* dev, JoystickController *joystick, gui::IGUIElement* parent, s32 id, IMenuManager *menumgr, - InventoryManager *invmgr, IGameDef *gamedef, + Client *client, ISimpleTextureSource *tsrc, IFormSource* fsrc, TextDest* tdst, - Client* client, bool remap_dbl_click) : + bool remap_dbl_click) : GUIModalMenu(dev->getGUIEnvironment(), parent, id, menumgr), m_device(dev), - m_invmgr(invmgr), - m_gamedef(gamedef), + m_invmgr(client), m_tsrc(tsrc), m_client(client), m_selected_item(NULL), @@ -307,8 +306,8 @@ void GUIFormSpecMenu::parseContainerEnd(parserData* data) void GUIFormSpecMenu::parseList(parserData* data,std::string element) { - if (m_gamedef == 0) { - warningstream<<"invalid use of 'list' with m_gamedef==0"<explicit_size) warningstream<<"invalid use of item_image_button without a size[] element"<idef(); + IItemDefManager *idef = m_client->idef(); ItemStack item; item.deSerialize(item_name, idef); @@ -2297,14 +2296,14 @@ void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase, if(!item.empty()) { drawItemStack(driver, m_font, item, - rect, &AbsoluteClippingRect, m_gamedef, + rect, &AbsoluteClippingRect, m_client, rotation_kind); } // Draw tooltip std::wstring tooltip_text = L""; if (hovering && !m_selected_item) { - tooltip_text = utf8_to_wide(item.getDefinition(m_gamedef->idef()).description); + tooltip_text = utf8_to_wide(item.getDefinition(m_client->idef()).description); } if (tooltip_text != L"") { std::vector tt_rows = str_split(tooltip_text, L'\n'); @@ -2349,7 +2348,7 @@ void GUIFormSpecMenu::drawSelectedItem() if (!m_selected_item) { drawItemStack(driver, m_font, ItemStack(), core::rect(v2s32(0, 0), v2s32(0, 0)), - NULL, m_gamedef, IT_ROT_DRAGGED); + NULL, m_client, IT_ROT_DRAGGED); return; } @@ -2363,7 +2362,7 @@ void GUIFormSpecMenu::drawSelectedItem() core::rect imgrect(0,0,imgsize.X,imgsize.Y); core::rect rect = imgrect + (m_pointer - imgrect.getCenter()); rect.constrainTo(driver->getViewPort()); - drawItemStack(driver, m_font, stack, rect, NULL, m_gamedef, IT_ROT_DRAGGED); + drawItemStack(driver, m_font, stack, rect, NULL, m_client, IT_ROT_DRAGGED); } void GUIFormSpecMenu::drawMenu() @@ -2488,11 +2487,11 @@ void GUIFormSpecMenu::drawMenu() */ for(u32 i=0; iidef(); + IItemDefManager *idef = m_client->idef(); ItemStack item; item.deSerialize(spec.item_name, idef); core::rect imgrect(0, 0, spec.geom.X, spec.geom.Y); @@ -2509,7 +2508,7 @@ void GUIFormSpecMenu::drawMenu() #endif } drawItemStack(driver, m_font, item, rect, &AbsoluteClippingRect, - m_gamedef, IT_ROT_NONE); + m_client, IT_ROT_NONE); } /* @@ -2527,7 +2526,7 @@ void GUIFormSpecMenu::drawMenu() if (!item_hovered) { drawItemStack(driver, m_font, ItemStack(), core::rect(v2s32(0, 0), v2s32(0, 0)), - NULL, m_gamedef, IT_ROT_HOVERED); + NULL, m_client, IT_ROT_HOVERED); } /* TODO find way to show tooltips on touchscreen */ @@ -3470,7 +3469,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) // Check how many items can be moved move_amount = stack_from.count = MYMIN(move_amount, stack_from.count); - ItemStack leftover = stack_to.addItem(stack_from, m_gamedef->idef()); + ItemStack leftover = stack_to.addItem(stack_from, m_client->idef()); // If source stack cannot be added to destination stack at all, // they are swapped if ((leftover.count == stack_from.count) && diff --git a/src/guiFormSpecMenu.h b/src/guiFormSpecMenu.h index 95df11e6a..94b52e6f0 100644 --- a/src/guiFormSpecMenu.h +++ b/src/guiFormSpecMenu.h @@ -34,7 +34,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/string.h" #include "util/enriched_string.h" -class IGameDef; class InventoryManager; class ISimpleTextureSource; class Client; @@ -289,12 +288,10 @@ public: JoystickController *joystick, gui::IGUIElement* parent, s32 id, IMenuManager *menumgr, - InventoryManager *invmgr, - IGameDef *gamedef, + Client *client, ISimpleTextureSource *tsrc, IFormSource* fs_src, TextDest* txt_dst, - Client* client, bool remap_dbl_click = true); ~GUIFormSpecMenu(); @@ -384,7 +381,6 @@ protected: irr::IrrlichtDevice* m_device; InventoryManager *m_invmgr; - IGameDef *m_gamedef; ISimpleTextureSource *m_tsrc; Client *m_client; diff --git a/src/hud.cpp b/src/hud.cpp index 43d957380..a602125e3 100644 --- a/src/hud.cpp +++ b/src/hud.cpp @@ -22,10 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "hud.h" #include "settings.h" #include "util/numeric.h" -#include "util/string.h" #include "log.h" -#include "gamedef.h" -#include "itemdef.h" +#include "client.h" #include "inventory.h" #include "client/tile.h" #include "localplayer.h" @@ -41,13 +39,13 @@ with this program; if not, write to the Free Software Foundation, Inc., #endif Hud::Hud(video::IVideoDriver *driver, scene::ISceneManager* smgr, - gui::IGUIEnvironment* guienv, IGameDef *gamedef, LocalPlayer *player, + gui::IGUIEnvironment* guienv, Client *client, LocalPlayer *player, Inventory *inventory) { this->driver = driver; this->smgr = smgr; this->guienv = guienv; - this->gamedef = gamedef; + this->client = client; this->player = player; this->inventory = inventory; @@ -61,7 +59,7 @@ Hud::Hud(video::IVideoDriver *driver, scene::ISceneManager* smgr, for (unsigned int i = 0; i < 4; i++) hbar_colors[i] = video::SColor(255, 255, 255, 255); - tsrc = gamedef->getTextureSource(); + tsrc = client->getTextureSource(); v3f crosshair_color = g_settings->getV3F("crosshair_color"); u32 cross_r = rangelim(myround(crosshair_color.X), 0, 255); @@ -92,7 +90,7 @@ Hud::Hud(video::IVideoDriver *driver, scene::ISceneManager* smgr, m_selection_material.Lighting = false; if (g_settings->getBool("enable_shaders")) { - IShaderSource *shdrsrc = gamedef->getShaderSource(); + IShaderSource *shdrsrc = client->getShaderSource(); u16 shader_id = shdrsrc->getShader( mode == "halo" ? "selection_shader" : "default_shader", 1, 1); m_selection_material.MaterialType = shdrsrc->getShaderInfo(shader_id).material; @@ -193,7 +191,7 @@ void Hud::drawItem(const ItemStack &item, const core::rect& rect, if (!use_hotbar_image) driver->draw2DRectangle(bgcolor2, rect, NULL); drawItemStack(driver, g_fontengine->getFont(), item, rect, NULL, - gamedef, selected ? IT_ROT_SELECTED : IT_ROT_NONE); + client, selected ? IT_ROT_SELECTED : IT_ROT_NONE); } //NOTE: selectitem = 0 -> no selected; selectitem 1-based @@ -629,7 +627,7 @@ void drawItemStack(video::IVideoDriver *driver, const ItemStack &item, const core::rect &rect, const core::rect *clip, - IGameDef *gamedef, + Client *client, ItemRotationKind rotation_kind) { static MeshTimeInfo rotation_time_infos[IT_ROT_NONE]; @@ -643,8 +641,8 @@ void drawItemStack(video::IVideoDriver *driver, return; } - const ItemDefinition &def = item.getDefinition(gamedef->idef()); - scene::IMesh* mesh = gamedef->idef()->getWieldMesh(def.name, gamedef); + const ItemDefinition &def = item.getDefinition(client->idef()); + scene::IMesh* mesh = client->idef()->getWieldMesh(def.name, client); if (mesh) { driver->clearZBuffer(); diff --git a/src/hud.h b/src/hud.h index a4d7990e9..efa0c3648 100644 --- a/src/hud.h +++ b/src/hud.h @@ -95,7 +95,7 @@ struct HudElement { #include #include "irr_aabb3d.h" -class IGameDef; +class Client; class ITextureSource; class Inventory; class InventoryList; @@ -107,7 +107,7 @@ public: video::IVideoDriver *driver; scene::ISceneManager* smgr; gui::IGUIEnvironment *guienv; - IGameDef *gamedef; + Client *client; LocalPlayer *player; Inventory *inventory; ITextureSource *tsrc; @@ -121,7 +121,7 @@ public: bool use_hotbar_selected_image; Hud(video::IVideoDriver *driver,scene::ISceneManager* smgr, - gui::IGUIEnvironment* guienv, IGameDef *gamedef, LocalPlayer *player, + gui::IGUIEnvironment* guienv, Client *client, LocalPlayer *player, Inventory *inventory); ~Hud(); @@ -190,7 +190,7 @@ void drawItemStack(video::IVideoDriver *driver, const ItemStack &item, const core::rect &rect, const core::rect *clip, - IGameDef *gamedef, + Client *client, ItemRotationKind rotation_kind); #endif diff --git a/src/itemdef.cpp b/src/itemdef.cpp index 1aa6331dc..5ba9d8f9a 100644 --- a/src/itemdef.cpp +++ b/src/itemdef.cpp @@ -20,7 +20,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "itemdef.h" -#include "gamedef.h" #include "nodedef.h" #include "tool.h" #include "inventory.h" @@ -29,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mesh.h" #include "wieldmesh.h" #include "client/tile.h" +#include "client.h" #endif #include "log.h" #include "settings.h" @@ -317,7 +317,7 @@ public: #ifndef SERVER public: ClientCached* createClientCachedDirect(const std::string &name, - IGameDef *gamedef) const + Client *client) const { infostream<<"Lazily creating item texture and mesh for \"" <getTextureSource(); + ITextureSource *tsrc = client->getTextureSource(); const ItemDefinition &def = get(name); // Create new ClientCached @@ -345,7 +345,7 @@ public: ItemStack item = ItemStack(); item.name = def.name; - scene::IMesh *mesh = getItemMesh(gamedef, item); + scene::IMesh *mesh = getItemMesh(client, item); cc->wield_mesh = mesh; // Put in cache @@ -354,7 +354,7 @@ public: return cc; } ClientCached* getClientCached(const std::string &name, - IGameDef *gamedef) const + Client *client) const { ClientCached *cc = NULL; m_clientcached.get(name, &cc); @@ -363,7 +363,7 @@ public: if(thr_is_current_thread(m_main_thread)) { - return createClientCachedDirect(name, gamedef); + return createClientCachedDirect(name, client); } else { @@ -392,18 +392,18 @@ public: } // Get item inventory texture virtual video::ITexture* getInventoryTexture(const std::string &name, - IGameDef *gamedef) const + Client *client) const { - ClientCached *cc = getClientCached(name, gamedef); + ClientCached *cc = getClientCached(name, client); if(!cc) return NULL; return cc->inventory_texture; } // Get item wield mesh virtual scene::IMesh* getWieldMesh(const std::string &name, - IGameDef *gamedef) const + Client *client) const { - ClientCached *cc = getClientCached(name, gamedef); + ClientCached *cc = getClientCached(name, client); if(!cc) return NULL; return cc->wield_mesh; @@ -543,7 +543,7 @@ public: request = m_get_clientcached_queue.pop(); m_get_clientcached_queue.pushResult(request, - createClientCachedDirect(request.key, gamedef)); + createClientCachedDirect(request.key, (Client *)gamedef)); } #endif } diff --git a/src/itemdef.h b/src/itemdef.h index dcb98e8a9..2ade6116a 100644 --- a/src/itemdef.h +++ b/src/itemdef.h @@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "itemgroup.h" #include "sound.h" class IGameDef; +class Client; struct ToolCapabilities; /* @@ -107,10 +108,10 @@ public: #ifndef SERVER // Get item inventory texture virtual video::ITexture* getInventoryTexture(const std::string &name, - IGameDef *gamedef) const=0; + Client *client) const=0; // Get item wield mesh virtual scene::IMesh* getWieldMesh(const std::string &name, - IGameDef *gamedef) const=0; + Client *client) const=0; #endif virtual void serialize(std::ostream &os, u16 protocol_version)=0; @@ -133,10 +134,10 @@ public: #ifndef SERVER // Get item inventory texture virtual video::ITexture* getInventoryTexture(const std::string &name, - IGameDef *gamedef) const=0; + Client *client) const=0; // Get item wield mesh virtual scene::IMesh* getWieldMesh(const std::string &name, - IGameDef *gamedef) const=0; + Client *client) const=0; #endif // Remove all registered item and node definitions and aliases diff --git a/src/localplayer.cpp b/src/localplayer.cpp index 4d0ca0600..b859c6455 100644 --- a/src/localplayer.cpp +++ b/src/localplayer.cpp @@ -21,7 +21,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "event.h" #include "collision.h" -#include "gamedef.h" #include "nodedef.h" #include "settings.h" #include "environment.h" @@ -32,8 +31,8 @@ with this program; if not, write to the Free Software Foundation, Inc., LocalPlayer */ -LocalPlayer::LocalPlayer(Client *gamedef, const char *name): - Player(name, gamedef->idef()), +LocalPlayer::LocalPlayer(Client *client, const char *name): + Player(name, client->idef()), parent(0), hp(PLAYER_MAX_HP), got_teleported(false), @@ -79,7 +78,7 @@ LocalPlayer::LocalPlayer(Client *gamedef, const char *name): camera_barely_in_ceiling(false), m_collisionbox(-BS * 0.30, 0.0, -BS * 0.30, BS * 0.30, BS * 1.75, BS * 0.30), m_cao(NULL), - m_gamedef(gamedef) + m_client(client) { // Initialize hp to 0, so that no hearts will be shown if server // doesn't support health points @@ -96,7 +95,7 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, std::vector *collision_info) { Map *map = &env->getMap(); - INodeDefManager *nodemgr = m_gamedef->ndef(); + INodeDefManager *nodemgr = m_client->ndef(); v3f position = getPosition(); @@ -109,8 +108,8 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, } // Skip collision detection if noclip mode is used - bool fly_allowed = m_gamedef->checkLocalPrivilege("fly"); - bool noclip = m_gamedef->checkLocalPrivilege("noclip") && + bool fly_allowed = m_client->checkLocalPrivilege("fly"); + bool noclip = m_client->checkLocalPrivilege("noclip") && g_settings->getBool("noclip"); bool free_move = noclip && fly_allowed && g_settings->getBool("free_move"); if (free_move) { @@ -241,7 +240,7 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, v3f accel_f = v3f(0,0,0); - collisionMoveResult result = collisionMoveSimple(env, m_gamedef, + collisionMoveResult result = collisionMoveSimple(env, m_client, pos_max_d, m_collisionbox, player_stepheight, dtime, &position, &m_speed, accel_f); @@ -376,7 +375,7 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, if(!result.standing_on_object && !touching_ground_was && touching_ground) { MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround"); - m_gamedef->event()->put(e); + m_client->event()->put(e); // Set camera impact value to be used for view bobbing camera_impact = getSpeed().Y * -1; @@ -448,8 +447,8 @@ void LocalPlayer::applyControl(float dtime) v3f speedH = v3f(0,0,0); // Horizontal (X, Z) v3f speedV = v3f(0,0,0); // Vertical (Y) - bool fly_allowed = m_gamedef->checkLocalPrivilege("fly"); - bool fast_allowed = m_gamedef->checkLocalPrivilege("fast"); + bool fly_allowed = m_client->checkLocalPrivilege("fly"); + bool fast_allowed = m_client->checkLocalPrivilege("fast"); bool free_move = fly_allowed && g_settings->getBool("free_move"); bool fast_move = fast_allowed && g_settings->getBool("fast_move"); @@ -599,7 +598,7 @@ void LocalPlayer::applyControl(float dtime) setSpeed(speedJ); MtEvent *e = new SimpleTriggerEvent("PlayerJump"); - m_gamedef->event()->put(e); + m_client->event()->put(e); } } else if(in_liquid) diff --git a/src/localplayer.h b/src/localplayer.h index 7a1cb7466..cbdcb9867 100644 --- a/src/localplayer.h +++ b/src/localplayer.h @@ -35,7 +35,7 @@ enum LocalPlayerAnimations {NO_ANIM, WALK_ANIM, DIG_ANIM, WD_ANIM}; // no local class LocalPlayer : public Player { public: - LocalPlayer(Client *gamedef, const char *name); + LocalPlayer(Client *client, const char *name); virtual ~LocalPlayer(); ClientActiveObject *parent; @@ -162,7 +162,7 @@ private: aabb3f m_collisionbox; GenericCAO* m_cao; - Client *m_gamedef; + Client *m_client; }; #endif diff --git a/src/mapblock_mesh.cpp b/src/mapblock_mesh.cpp index 977eabb6e..143adb410 100644 --- a/src/mapblock_mesh.cpp +++ b/src/mapblock_mesh.cpp @@ -23,7 +23,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "map.h" #include "profiler.h" #include "nodedef.h" -#include "gamedef.h" #include "mesh.h" #include "minimap.h" #include "content_mapblock.h" @@ -43,14 +42,14 @@ static void applyFacesShading(video::SColor &color, const float factor) MeshMakeData */ -MeshMakeData::MeshMakeData(IGameDef *gamedef, bool use_shaders, +MeshMakeData::MeshMakeData(Client *client, bool use_shaders, bool use_tangent_vertices): m_vmanip(), m_blockpos(-1337,-1337,-1337), m_crack_pos_relative(-1337, -1337, -1337), m_smooth_lighting(false), m_show_hud(false), - m_gamedef(gamedef), + m_client(client), m_use_shaders(use_shaders), m_use_tangent_vertices(use_tangent_vertices) {} @@ -233,7 +232,7 @@ static u16 getSmoothLightCombined(v3s16 p, MeshMakeData *data) v3s16(1,1,1), }; - INodeDefManager *ndef = data->m_gamedef->ndef(); + INodeDefManager *ndef = data->m_client->ndef(); u16 ambient_occlusion = 0; u16 light_count = 0; @@ -664,7 +663,7 @@ static u8 face_contents(content_t m1, content_t m2, bool *equivalent, */ TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data) { - INodeDefManager *ndef = data->m_gamedef->ndef(); + INodeDefManager *ndef = data->m_client->ndef(); TileSpec spec = ndef->get(mn).tiles[tileindex]; // Apply temporary crack if (p == data->m_crack_pos_relative) @@ -677,7 +676,7 @@ TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data) */ TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data) { - INodeDefManager *ndef = data->m_gamedef->ndef(); + INodeDefManager *ndef = data->m_client->ndef(); // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0), // (0,0,1), (0,0,-1) or (0,0,0) @@ -734,7 +733,7 @@ TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data) u16 tile_index=facedir*16 + dir_i; TileSpec spec = getNodeTileN(mn, p, dir_to_tile[tile_index], data); spec.rotation=dir_to_tile[tile_index + 1]; - spec.texture = data->m_gamedef->tsrc()->getTexture(spec.texture_id); + spec.texture = data->m_client->tsrc()->getTexture(spec.texture_id); return spec; } @@ -753,7 +752,7 @@ static void getTileInfo( ) { VoxelManipulator &vmanip = data->m_vmanip; - INodeDefManager *ndef = data->m_gamedef->ndef(); + INodeDefManager *ndef = data->m_client->ndef(); v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE; MapNode &n0 = vmanip.getNodeRefUnsafe(blockpos_nodes + p); @@ -1020,10 +1019,10 @@ static void updateAllFastFaceRows(MeshMakeData *data, MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset): m_mesh(new scene::SMesh()), m_minimap_mapblock(NULL), - m_gamedef(data->m_gamedef), - m_driver(m_gamedef->tsrc()->getDevice()->getVideoDriver()), - m_tsrc(m_gamedef->getTextureSource()), - m_shdrsrc(m_gamedef->getShaderSource()), + m_client(data->m_client), + m_driver(m_client->tsrc()->getDevice()->getVideoDriver()), + m_tsrc(m_client->getTextureSource()), + m_shdrsrc(m_client->getShaderSource()), m_animation_force_timer(0), // force initial animation m_last_crack(-1), m_crack_materials(), @@ -1243,7 +1242,7 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset): if (m_use_tangent_vertices) { scene::IMeshManipulator* meshmanip = - m_gamedef->getSceneManager()->getMeshManipulator(); + m_client->getSceneManager()->getMeshManipulator(); meshmanip->recalculateTangents(m_mesh, true, false, false); } diff --git a/src/mapblock_mesh.h b/src/mapblock_mesh.h index 8376468da..5adb7df3f 100644 --- a/src/mapblock_mesh.h +++ b/src/mapblock_mesh.h @@ -26,7 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/cpp11_container.h" #include -class IGameDef; +class Client; class IShaderSource; /* @@ -45,11 +45,11 @@ struct MeshMakeData bool m_smooth_lighting; bool m_show_hud; - IGameDef *m_gamedef; + Client *m_client; bool m_use_shaders; bool m_use_tangent_vertices; - MeshMakeData(IGameDef *gamedef, bool use_shaders, + MeshMakeData(Client *client, bool use_shaders, bool use_tangent_vertices = false); /* @@ -128,7 +128,7 @@ public: private: scene::IMesh *m_mesh; MinimapMapblock *m_minimap_mapblock; - IGameDef *m_gamedef; + Client *m_client; video::IVideoDriver *m_driver; ITextureSource *m_tsrc; IShaderSource *m_shdrsrc; diff --git a/src/mg_biome.cpp b/src/mg_biome.cpp index 78034bf6c..d564e9415 100644 --- a/src/mg_biome.cpp +++ b/src/mg_biome.cpp @@ -20,10 +20,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mg_biome.h" #include "mg_decoration.h" #include "emerge.h" -#include "gamedef.h" +#include "server.h" #include "nodedef.h" #include "map.h" //for MMVManip -#include "log.h" #include "util/numeric.h" #include "util/mathconstants.h" #include "porting.h" @@ -33,10 +32,10 @@ with this program; if not, write to the Free Software Foundation, Inc., /////////////////////////////////////////////////////////////////////////////// -BiomeManager::BiomeManager(IGameDef *gamedef) : - ObjDefManager(gamedef, OBJDEF_BIOME) +BiomeManager::BiomeManager(Server *server) : + ObjDefManager(server, OBJDEF_BIOME) { - m_gamedef = gamedef; + m_server = server; // Create default biome to be used in case none exist Biome *b = new Biome; @@ -73,7 +72,7 @@ BiomeManager::~BiomeManager() void BiomeManager::clear() { - EmergeManager *emerge = m_gamedef->getEmergeManager(); + EmergeManager *emerge = m_server->getEmergeManager(); // Remove all dangling references in Decorations DecorationManager *decomgr = emerge->decomgr; diff --git a/src/mg_biome.h b/src/mg_biome.h index a10193bc3..15088f7dd 100644 --- a/src/mg_biome.h +++ b/src/mg_biome.h @@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "nodedef.h" #include "noise.h" +class Server; class Settings; class BiomeManager; @@ -186,7 +187,7 @@ private: class BiomeManager : public ObjDefManager { public: - BiomeManager(IGameDef *gamedef); + BiomeManager(Server *server); virtual ~BiomeManager(); const char *getObjectTitle() const @@ -223,7 +224,7 @@ public: virtual void clear(); private: - IGameDef *m_gamedef; + Server *m_server; }; diff --git a/src/mg_schematic.cpp b/src/mg_schematic.cpp index e028215dc..3d08d86fa 100644 --- a/src/mg_schematic.cpp +++ b/src/mg_schematic.cpp @@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include "mg_schematic.h" -#include "gamedef.h" +#include "server.h" #include "mapgen.h" #include "emerge.h" #include "map.h" @@ -34,16 +34,16 @@ with this program; if not, write to the Free Software Foundation, Inc., /////////////////////////////////////////////////////////////////////////////// -SchematicManager::SchematicManager(IGameDef *gamedef) : - ObjDefManager(gamedef, OBJDEF_SCHEMATIC) +SchematicManager::SchematicManager(Server *server) : + ObjDefManager(server, OBJDEF_SCHEMATIC) { - m_gamedef = gamedef; + m_server = server; } void SchematicManager::clear() { - EmergeManager *emerge = m_gamedef->getEmergeManager(); + EmergeManager *emerge = m_server->getEmergeManager(); // Remove all dangling references in Decorations DecorationManager *decomgr = emerge->decomgr; diff --git a/src/mg_schematic.h b/src/mg_schematic.h index da8859540..1d46e6ac4 100644 --- a/src/mg_schematic.h +++ b/src/mg_schematic.h @@ -29,7 +29,7 @@ class Mapgen; class MMVManip; class PseudoRandom; class NodeResolver; -class IGameDef; +class Server; /* Minetest Schematic File Format @@ -123,7 +123,7 @@ public: class SchematicManager : public ObjDefManager { public: - SchematicManager(IGameDef *gamedef); + SchematicManager(Server *server); virtual ~SchematicManager() {} virtual void clear(); @@ -139,7 +139,7 @@ public: } private: - IGameDef *m_gamedef; + Server *m_server; }; void generate_nodelist_and_update_ids(MapNode *nodes, size_t nodecount, diff --git a/src/nodedef.cpp b/src/nodedef.cpp index dbbdf95d2..b7d023897 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #ifndef SERVER #include "client/tile.h" #include "mesh.h" +#include "client.h" #include #endif #include "log.h" @@ -572,8 +573,7 @@ void ContentFeatures::fillTileAttribs(ITextureSource *tsrc, TileSpec *tile, #ifndef SERVER void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc, - scene::ISceneManager *smgr, scene::IMeshManipulator *meshmanip, - IGameDef *gamedef, const TextureSettings &tsettings) + scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings) { // minimap pixel color - the average color of a texture if (tsettings.enable_minimap && tiledef[0].name != "") @@ -709,7 +709,7 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc if ((drawtype == NDT_MESH) && (mesh != "")) { // Meshnode drawtype // Read the mesh and apply scale - mesh_ptr[0] = gamedef->getMesh(mesh); + mesh_ptr[0] = client->getMesh(mesh); if (mesh_ptr[0]){ v3f scale = v3f(1.0, 1.0, 1.0) * BS * visual_scale; scaleMesh(mesh_ptr[0], scale); @@ -1316,9 +1316,11 @@ void CNodeDefManager::updateTextures(IGameDef *gamedef, #ifndef SERVER infostream << "CNodeDefManager::updateTextures(): Updating " "textures in node definitions" << std::endl; - ITextureSource *tsrc = gamedef->tsrc(); - IShaderSource *shdsrc = gamedef->getShaderSource(); - scene::ISceneManager* smgr = gamedef->getSceneManager(); + + Client *client = (Client *)gamedef; + ITextureSource *tsrc = client->tsrc(); + IShaderSource *shdsrc = client->getShaderSource(); + scene::ISceneManager* smgr = client->getSceneManager(); scene::IMeshManipulator* meshmanip = smgr->getMeshManipulator(); TextureSettings tsettings; tsettings.readSettings(); @@ -1326,7 +1328,7 @@ void CNodeDefManager::updateTextures(IGameDef *gamedef, u32 size = m_content_features.size(); for (u32 i = 0; i < size; i++) { - m_content_features[i].updateTextures(tsrc, shdsrc, smgr, meshmanip, gamedef, tsettings); + m_content_features[i].updateTextures(tsrc, shdsrc, meshmanip, client, tsettings); progress_callback(progress_callback_args, i, size); } #endif diff --git a/src/nodedef.h b/src/nodedef.h index 284c4a198..183b95d87 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #ifndef SERVER #include "client/tile.h" #include "shader.h" +class Client; #endif #include "itemgroup.h" #include "sound.h" // SimpleSoundSpec @@ -322,8 +323,7 @@ struct ContentFeatures u32 shader_id, bool use_normal_texture, bool backface_culling, u8 alpha, u8 material_type); void updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc, - scene::ISceneManager *smgr, scene::IMeshManipulator *meshmanip, - IGameDef *gamedef, const TextureSettings &tsettings); + scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings); #endif }; diff --git a/src/particles.cpp b/src/particles.cpp index 97f42e2c4..d9eb3cfa5 100644 --- a/src/particles.cpp +++ b/src/particles.cpp @@ -18,11 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #include "particles.h" -#include "constants.h" -#include "debug.h" -#include "settings.h" -#include "client/tile.h" -#include "gamedef.h" +#include "client.h" #include "collision.h" #include #include "util/numeric.h" @@ -452,7 +448,7 @@ void ParticleManager::clearAll () } } -void ParticleManager::handleParticleEvent(ClientEvent *event, IGameDef *gamedef, +void ParticleManager::handleParticleEvent(ClientEvent *event, Client *client, scene::ISceneManager* smgr, LocalPlayer *player) { switch (event->type) { @@ -477,9 +473,9 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, IGameDef *gamedef, } video::ITexture *texture = - gamedef->tsrc()->getTextureForMesh(*(event->add_particlespawner.texture)); + client->tsrc()->getTextureForMesh(*(event->add_particlespawner.texture)); - ParticleSpawner* toadd = new ParticleSpawner(gamedef, smgr, player, + ParticleSpawner* toadd = new ParticleSpawner(client, smgr, player, event->add_particlespawner.amount, event->add_particlespawner.spawntime, *event->add_particlespawner.minpos, @@ -520,9 +516,9 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, IGameDef *gamedef, } case CE_SPAWN_PARTICLE: { video::ITexture *texture = - gamedef->tsrc()->getTextureForMesh(*(event->spawn_particle.texture)); + client->tsrc()->getTextureForMesh(*(event->spawn_particle.texture)); - Particle* toadd = new Particle(gamedef, smgr, player, m_env, + Particle* toadd = new Particle(client, smgr, player, m_env, *event->spawn_particle.pos, *event->spawn_particle.vel, *event->spawn_particle.acc, diff --git a/src/particles.h b/src/particles.h index eb8c6665d..00cb2c08e 100644 --- a/src/particles.h +++ b/src/particles.h @@ -170,7 +170,7 @@ public: void step (float dtime); - void handleParticleEvent(ClientEvent *event,IGameDef *gamedef, + void handleParticleEvent(ClientEvent *event, Client *client, scene::ISceneManager* smgr, LocalPlayer *player); void addDiggingParticles(IGameDef* gamedef, scene::ISceneManager* smgr, diff --git a/src/pathfinder.cpp b/src/pathfinder.cpp index 84aa9252c..b240ec21f 100644 --- a/src/pathfinder.cpp +++ b/src/pathfinder.cpp @@ -24,12 +24,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "pathfinder.h" #include "serverenvironment.h" -#include "gamedef.h" +#include "server.h" #include "nodedef.h" -#include "map.h" -#include "log.h" -#include "irr_aabb3d.h" -#include "util/basic_macros.h" //#define PATHFINDER_DEBUG //#define PATHFINDER_CALC_TIME diff --git a/src/script/lua_api/l_nodemeta.cpp b/src/script/lua_api/l_nodemeta.cpp index 3cdd3cbfe..3d03c0c41 100644 --- a/src/script/lua_api/l_nodemeta.cpp +++ b/src/script/lua_api/l_nodemeta.cpp @@ -20,14 +20,10 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "lua_api/l_nodemeta.h" #include "lua_api/l_internal.h" #include "lua_api/l_inventory.h" -#include "common/c_converter.h" #include "common/c_content.h" #include "serverenvironment.h" #include "map.h" -#include "gamedef.h" -#include "nodemetadata.h" - - +#include "server.h" /* NodeMetaRef diff --git a/src/server.cpp b/src/server.cpp index 60dbef0d2..7380d37c2 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -50,7 +50,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "content_abm.h" #include "content_sao.h" #include "mods.h" -#include "sound.h" // dummySoundManager #include "event_manager.h" #include "serverlist.h" #include "util/string.h" @@ -3310,29 +3309,12 @@ ICraftDefManager *Server::getCraftDefManager() { return m_craftdef; } -ITextureSource *Server::getTextureSource() -{ - return NULL; -} -IShaderSource *Server::getShaderSource() -{ - return NULL; -} -scene::ISceneManager *Server::getSceneManager() -{ - return NULL; -} u16 Server::allocateUnknownNodeId(const std::string &name) { return m_nodedef->allocateDummy(name); } -ISoundManager *Server::getSoundManager() -{ - return &dummySoundManager; -} - MtEventManager *Server::getEventManager() { return m_event; diff --git a/src/server.h b/src/server.h index fe7b50b77..a86f75f1d 100644 --- a/src/server.h +++ b/src/server.h @@ -283,13 +283,9 @@ public: virtual IItemDefManager* getItemDefManager(); virtual INodeDefManager* getNodeDefManager(); virtual ICraftDefManager* getCraftDefManager(); - virtual ITextureSource* getTextureSource(); - virtual IShaderSource* getShaderSource(); virtual u16 allocateUnknownNodeId(const std::string &name); - virtual ISoundManager* getSoundManager(); virtual MtEventManager* getEventManager(); - virtual scene::ISceneManager* getSceneManager(); - virtual IRollbackManager *getRollbackManager() { return m_rollback; } + IRollbackManager *getRollbackManager() { return m_rollback; } virtual EmergeManager *getEmergeManager() { return m_emerge; } IWritableItemDefManager* getWritableItemDefManager(); diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index c9fa64ec5..e1962bcff 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -352,11 +352,11 @@ void ActiveBlockList::update(std::vector &active_positions, */ ServerEnvironment::ServerEnvironment(ServerMap *map, - GameScripting *scriptIface, IGameDef *gamedef, + GameScripting *scriptIface, Server *server, const std::string &path_world) : m_map(map), m_script(scriptIface), - m_gamedef(gamedef), + m_server(server), m_path_world(path_world), m_send_recommended_timer(0), m_active_block_interval_overload_skip(0), @@ -487,7 +487,7 @@ void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason, for (std::vector::iterator it = m_players.begin(); it != m_players.end(); ++it) { RemotePlayer *player = dynamic_cast(*it); - ((Server*)m_gamedef)->DenyAccessVerCompliant(player->peer_id, + m_server->DenyAccessVerCompliant(player->peer_id, player->protocol_version, reason, str_reason, reconnect); } } @@ -501,7 +501,7 @@ void ServerEnvironment::saveLoadedPlayers() it != m_players.end(); ++it) { if ((*it)->checkModified()) { - (*it)->save(players_path, m_gamedef); + (*it)->save(players_path, m_server); } } } @@ -511,7 +511,7 @@ void ServerEnvironment::savePlayer(RemotePlayer *player) std::string players_path = m_path_world + DIR_DELIM "players"; fs::CreateDir(players_path); - player->save(players_path, m_gamedef); + player->save(players_path, m_server); } RemotePlayer *ServerEnvironment::loadPlayer(const std::string &playername, PlayerSAO *sao) @@ -523,7 +523,7 @@ RemotePlayer *ServerEnvironment::loadPlayer(const std::string &playername, Playe RemotePlayer *player = getPlayer(playername.c_str()); if (!player) { - player = new RemotePlayer("", m_gamedef->idef()); + player = new RemotePlayer("", m_server->idef()); newplayer = true; } @@ -632,7 +632,7 @@ void ServerEnvironment::loadMeta() } catch (SettingNotFoundException &e) { // No problem, this is expected. Just continue with an empty string } - m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_gamedef, m_game_time); + m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_server, m_game_time); m_day_count = args.exists("day_count") ? args.getU64("day_count") : 0; @@ -640,7 +640,7 @@ void ServerEnvironment::loadMeta() void ServerEnvironment::loadDefaultMeta() { - m_lbm_mgr.loadIntroductionTimes("", m_gamedef, m_game_time); + m_lbm_mgr.loadIntroductionTimes("", m_server, m_game_time); } struct ActiveABM @@ -902,7 +902,7 @@ void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm) bool ServerEnvironment::setNode(v3s16 p, const MapNode &n) { - INodeDefManager *ndef = m_gamedef->ndef(); + INodeDefManager *ndef = m_server->ndef(); MapNode n_old = m_map->getNodeNoEx(p); // Call destructor @@ -929,7 +929,7 @@ bool ServerEnvironment::setNode(v3s16 p, const MapNode &n) bool ServerEnvironment::removeNode(v3s16 p) { - INodeDefManager *ndef = m_gamedef->ndef(); + INodeDefManager *ndef = m_server->ndef(); MapNode n_old = m_map->getNodeNoEx(p); // Call destructor diff --git a/src/serverenvironment.h b/src/serverenvironment.h index 20a783ea5..d71d29a9c 100644 --- a/src/serverenvironment.h +++ b/src/serverenvironment.h @@ -29,6 +29,8 @@ class PlayerSAO; class ServerEnvironment; class ActiveBlockModifier; class ServerActiveObject; +class Server; +class GameScripting; /* {Active, Loading} block modifier interface. @@ -190,7 +192,7 @@ class ServerEnvironment : public Environment { public: ServerEnvironment(ServerMap *map, GameScripting *scriptIface, - IGameDef *gamedef, const std::string &path_world); + Server *server, const std::string &path_world); ~ServerEnvironment(); Map & getMap(); @@ -201,8 +203,8 @@ public: GameScripting* getScriptIface() { return m_script; } - IGameDef *getGameDef() - { return m_gamedef; } + Server *getGameDef() + { return m_server; } float getSendRecommendedInterval() { return m_recommended_send_interval; } @@ -377,8 +379,8 @@ private: ServerMap *m_map; // Lua state GameScripting* m_script; - // Game definition - IGameDef *m_gamedef; + // Server definition + Server *m_server; // World path const std::string m_path_world; // Active object list diff --git a/src/wieldmesh.cpp b/src/wieldmesh.cpp index 9c4d5b642..c305238fe 100644 --- a/src/wieldmesh.cpp +++ b/src/wieldmesh.cpp @@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "settings.h" #include "wieldmesh.h" #include "inventory.h" -#include "gamedef.h" +#include "client.h" #include "itemdef.h" #include "nodedef.h" #include "mesh.h" @@ -283,7 +283,7 @@ void WieldMeshSceneNode::setExtruded(const std::string &imagename, video::SMaterial &material = m_meshnode->getMaterial(0); material.setTexture(0, tsrc->getTextureForMesh(imagename)); material.TextureLayer[0].TextureWrapU = video::ETC_CLAMP_TO_EDGE; - material.TextureLayer[0].TextureWrapV = video::ETC_CLAMP_TO_EDGE; + material.TextureLayer[0].TextureWrapV = video::ETC_CLAMP_TO_EDGE; material.MaterialType = m_material_type; material.setFlag(video::EMF_BACK_FACE_CULLING, true); // Enable bi/trilinear filtering only for high resolution textures @@ -304,12 +304,12 @@ void WieldMeshSceneNode::setExtruded(const std::string &imagename, } } -void WieldMeshSceneNode::setItem(const ItemStack &item, IGameDef *gamedef) +void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client) { - ITextureSource *tsrc = gamedef->getTextureSource(); - IItemDefManager *idef = gamedef->getItemDefManager(); - IShaderSource *shdrsrc = gamedef->getShaderSource(); - INodeDefManager *ndef = gamedef->getNodeDefManager(); + ITextureSource *tsrc = client->getTextureSource(); + IItemDefManager *idef = client->getItemDefManager(); + IShaderSource *shdrsrc = client->getShaderSource(); + INodeDefManager *ndef = client->getNodeDefManager(); const ItemDefinition &def = item.getDefinition(idef); const ContentFeatures &f = ndef->get(def.name); content_t id = ndef->getId(def.name); @@ -341,7 +341,7 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, IGameDef *gamedef) } else if (f.drawtype == NDT_NORMAL || f.drawtype == NDT_ALLFACES) { setCube(f.tiles, def.wield_scale, tsrc); } else { - MeshMakeData mesh_make_data(gamedef, false); + MeshMakeData mesh_make_data(client, false); MapNode mesh_make_node(id, 255, 0); mesh_make_data.fillSingleNode(&mesh_make_node); MapBlockMesh mapblock_mesh(&mesh_make_data, v3s16(0, 0, 0)); @@ -435,11 +435,11 @@ void WieldMeshSceneNode::changeToMesh(scene::IMesh *mesh) m_meshnode->setVisible(true); } -scene::IMesh *getItemMesh(IGameDef *gamedef, const ItemStack &item) +scene::IMesh *getItemMesh(Client *client, const ItemStack &item) { - ITextureSource *tsrc = gamedef->getTextureSource(); - IItemDefManager *idef = gamedef->getItemDefManager(); - INodeDefManager *ndef = gamedef->getNodeDefManager(); + ITextureSource *tsrc = client->getTextureSource(); + IItemDefManager *idef = client->getItemDefManager(); + INodeDefManager *ndef = client->getNodeDefManager(); const ItemDefinition &def = item.getDefinition(idef); const ContentFeatures &f = ndef->get(def.name); content_t id = ndef->getId(def.name); @@ -470,7 +470,7 @@ scene::IMesh *getItemMesh(IGameDef *gamedef, const ItemStack &item) mesh = cloneMesh(g_extrusion_mesh_cache->createCube()); scaleMesh(mesh, v3f(1.2, 1.2, 1.2)); } else { - MeshMakeData mesh_make_data(gamedef, false); + MeshMakeData mesh_make_data(client, false); MapNode mesh_make_node(id, 255, 0); mesh_make_data.fillSingleNode(&mesh_make_node); MapBlockMesh mapblock_mesh(&mesh_make_data, v3s16(0, 0, 0)); diff --git a/src/wieldmesh.h b/src/wieldmesh.h index 0b3136bc1..0162c5e5a 100644 --- a/src/wieldmesh.h +++ b/src/wieldmesh.h @@ -24,7 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include struct ItemStack; -class IGameDef; +class Client; class ITextureSource; struct TileSpec; @@ -42,7 +42,7 @@ public: v3f wield_scale, ITextureSource *tsrc); void setExtruded(const std::string &imagename, v3f wield_scale, ITextureSource *tsrc, u8 num_frames); - void setItem(const ItemStack &item, IGameDef *gamedef); + void setItem(const ItemStack &item, Client *client); // Sets the vertex color of the wield mesh. // Must only be used if the constructor was called with lighting = false @@ -77,7 +77,7 @@ private: aabb3f m_bounding_box; }; -scene::IMesh *getItemMesh(IGameDef *gamedef, const ItemStack &item); +scene::IMesh *getItemMesh(Client *client, const ItemStack &item); scene::IMesh *getExtrudedMesh(ITextureSource *tsrc, const std::string &imagename); -- cgit v1.2.3 From 430d3b28e46b16e1411ec0ecb6c0d0ab9d6feb63 Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Wed, 11 Jan 2017 09:03:07 +0100 Subject: Cleanup some header inclusions to improve compilation times --- src/client.h | 1 + src/clientenvironment.cpp | 1 + src/environment.h | 4 ++-- src/guiFormSpecMenu.h | 1 - src/localplayer.cpp | 11 +++++++++++ src/localplayer.h | 11 ++--------- src/minimap.cpp | 1 + src/network/serverpackethandler.cpp | 1 + src/player.h | 1 - src/serverenvironment.h | 3 +++ 10 files changed, 22 insertions(+), 13 deletions(-) (limited to 'src/clientenvironment.cpp') diff --git a/src/client.h b/src/client.h index df3e7e605..f84246deb 100644 --- a/src/client.h +++ b/src/client.h @@ -34,6 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "localplayer.h" #include "hud.h" #include "particles.h" +#include "mapnode.h" struct MeshMakeData; class MapBlockMesh; diff --git a/src/clientenvironment.cpp b/src/clientenvironment.cpp index 65646c6b4..b32a02f2d 100644 --- a/src/clientenvironment.cpp +++ b/src/clientenvironment.cpp @@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "profiler.h" #include "raycast.h" #include "voxelalgorithms.h" +#include "settings.h" /* ClientEnvironment diff --git a/src/environment.h b/src/environment.h index 0cc3222f9..5154bbdcb 100644 --- a/src/environment.h +++ b/src/environment.h @@ -36,12 +36,12 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irr_v3d.h" #include "activeobject.h" #include "util/numeric.h" -#include "mapnode.h" -#include "mapblock.h" #include "threading/mutex.h" #include "threading/atomic.h" #include "network/networkprotocol.h" // for AccessDeniedCode +class Map; + class Environment { public: diff --git a/src/guiFormSpecMenu.h b/src/guiFormSpecMenu.h index 94b52e6f0..2ab7db4f1 100644 --- a/src/guiFormSpecMenu.h +++ b/src/guiFormSpecMenu.h @@ -25,7 +25,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "irrlichttypes_extrabloated.h" -#include "inventory.h" #include "inventorymanager.h" #include "modalMenu.h" #include "guiTable.h" diff --git a/src/localplayer.cpp b/src/localplayer.cpp index b859c6455..857d95d8b 100644 --- a/src/localplayer.cpp +++ b/src/localplayer.cpp @@ -655,6 +655,17 @@ v3s16 LocalPlayer::getStandingNodePos() return floatToInt(getPosition() - v3f(0, BS, 0), BS); } +v3s16 LocalPlayer::getLightPosition() const +{ + return floatToInt(m_position + v3f(0,BS+BS/2,0), BS); +} + +v3f LocalPlayer::getEyeOffset() const +{ + float eye_height = camera_barely_in_ceiling ? 1.5f : 1.625f; + return v3f(0, BS * eye_height, 0); +} + // Horizontal acceleration (X and Z), Y direction is ignored void LocalPlayer::accelerateHorizontal(const v3f &target_speed, const f32 max_increase) { diff --git a/src/localplayer.h b/src/localplayer.h index cbdcb9867..685a78cb3 100644 --- a/src/localplayer.h +++ b/src/localplayer.h @@ -105,10 +105,7 @@ public: u16 getBreath() const { return m_breath; } void setBreath(u16 breath) { m_breath = breath; } - v3s16 getLightPosition() const - { - return floatToInt(m_position + v3f(0,BS+BS/2,0), BS); - } + v3s16 getLightPosition() const; void setYaw(f32 yaw) { @@ -131,11 +128,7 @@ public: v3f getPosition() const { return m_position; } v3f getEyePosition() const { return m_position + getEyeOffset(); } - v3f getEyeOffset() const - { - float eye_height = camera_barely_in_ceiling ? 1.5f : 1.625f; - return v3f(0, BS * eye_height, 0); - } + v3f getEyeOffset() const; private: void accelerateHorizontal(const v3f &target_speed, const f32 max_increase); void accelerateVertical(const v3f &target_speed, const f32 max_increase); diff --git a/src/minimap.cpp b/src/minimap.cpp index 8cd0a7beb..f49adb517 100644 --- a/src/minimap.cpp +++ b/src/minimap.cpp @@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "porting.h" #include "util/numeric.h" #include "util/string.h" +#include "mapblock.h" #include diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index eeabcca71..408fe7706 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "content_abm.h" #include "content_sao.h" #include "emerge.h" +#include "mapblock.h" #include "nodedef.h" #include "player.h" #include "rollback_interface.h" diff --git a/src/player.h b/src/player.h index 5f9bb7ec9..3432069c0 100644 --- a/src/player.h +++ b/src/player.h @@ -22,7 +22,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irrlichttypes_bloated.h" #include "inventory.h" -#include "constants.h" // BS #include "threading/mutex.h" #include diff --git a/src/serverenvironment.h b/src/serverenvironment.h index d71d29a9c..b7056c00c 100644 --- a/src/serverenvironment.h +++ b/src/serverenvironment.h @@ -21,6 +21,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #define SERVER_ENVIRONMENT_HEADER #include "environment.h" +#include "mapnode.h" +#include "mapblock.h" +#include class IGameDef; class ServerMap; -- cgit v1.2.3 From d04d8aba7029a2501854a2838fd282b81358a54e Mon Sep 17 00:00:00 2001 From: Dániel Juhász Date: Thu, 12 Jan 2017 15:46:30 +0100 Subject: Add hardware node coloring. Includes: - Increase ContentFeatures serialization version - Color property and palettes for nodes - paramtype2 = "color", "colored facedir" or "colored wallmounted" --- client/shaders/nodes_shader/opengl_vertex.glsl | 43 +- .../water_surface_shader/opengl_vertex.glsl | 45 +- client/shaders/wielded_shader/opengl_vertex.glsl | 1 - doc/lua_api.txt | 27 +- src/client/tile.cpp | 3 - src/client/tile.h | 34 +- src/clientenvironment.cpp | 4 +- src/content_mapblock.cpp | 326 ++++++++----- src/game.cpp | 30 +- src/mapblock_mesh.cpp | 263 ++++++----- src/mapblock_mesh.h | 57 ++- src/mapnode.cpp | 20 +- src/mapnode.h | 12 +- src/mesh.cpp | 47 +- src/mesh.h | 14 +- src/minimap.cpp | 31 +- src/minimap.h | 4 +- src/network/networkprotocol.h | 5 +- src/nodedef.cpp | 513 +++++++++++++++++---- src/nodedef.h | 108 +++-- src/particles.cpp | 65 ++- src/particles.h | 19 +- src/script/common/c_content.cpp | 18 + src/script/cpp_api/s_node.cpp | 3 + src/shader.cpp | 2 +- src/wieldmesh.cpp | 62 ++- src/wieldmesh.h | 5 + 27 files changed, 1207 insertions(+), 554 deletions(-) (limited to 'src/clientenvironment.cpp') diff --git a/client/shaders/nodes_shader/opengl_vertex.glsl b/client/shaders/nodes_shader/opengl_vertex.glsl index 44c48cc4c..3ac79c26d 100644 --- a/client/shaders/nodes_shader/opengl_vertex.glsl +++ b/client/shaders/nodes_shader/opengl_vertex.glsl @@ -1,7 +1,8 @@ uniform mat4 mWorldViewProj; uniform mat4 mWorld; -uniform float dayNightRatio; +// Color of the light emitted by the sun. +uniform vec3 dayLight; uniform vec3 eyePosition; uniform float animationTimer; @@ -14,6 +15,8 @@ varying vec3 tsEyeVec; varying vec3 tsLightVec; varying float area_enable_parallax; +// Color of the light emitted by the light sources. +const vec3 artificialLight = vec3(1.04, 1.04, 1.04); const float e = 2.718281828459; const float BS = 10.0; @@ -119,31 +122,23 @@ float disp_z; v.z = dot(eyeVec, normal); tsEyeVec = normalize (v); + // Calculate color. + // Red, green and blue components are pre-multiplied with + // the brightness, so now we have to multiply these + // colors with the color of the incoming light. + // The pre-baked colors are halved to prevent overflow. vec4 color; - float day = gl_Color.r; - float night = gl_Color.g; - float light_source = gl_Color.b; - - float rg = mix(night, day, dayNightRatio); - rg += light_source * 2.5; // Make light sources brighter - float b = rg; - - // Moonlight is blue - b += (day - night) / 13.0; - rg -= (day - night) / 23.0; - + // The alpha gives the ratio of sunlight in the incoming light. + float nightRatio = 1 - gl_Color.a; + color.rgb = gl_Color.rgb * (gl_Color.a * dayLight.rgb + + nightRatio * artificialLight.rgb) * 2; + color.a = 1; + // Emphase blue a bit in darker places // See C++ implementation in mapblock_mesh.cpp finalColorBlend() - b += max(0.0, (1.0 - abs(b - 0.13) / 0.17) * 0.025); - - // Artificial light is yellow-ish - // See C++ implementation in mapblock_mesh.cpp finalColorBlend() - rg += max(0.0, (1.0 - abs(rg - 0.85) / 0.15) * 0.065); - - color.r = rg; - color.g = rg; - color.b = b; - - color.a = gl_Color.a; + float brightness = (color.r + color.g + color.b) / 3; + color.b += max(0.0, 0.021 - abs(0.2 * brightness - 0.021) + + 0.07 * brightness); + gl_FrontColor = gl_BackColor = clamp(color, 0.0, 1.0); } diff --git a/client/shaders/water_surface_shader/opengl_vertex.glsl b/client/shaders/water_surface_shader/opengl_vertex.glsl index a930e7b8f..112db9bb5 100644 --- a/client/shaders/water_surface_shader/opengl_vertex.glsl +++ b/client/shaders/water_surface_shader/opengl_vertex.glsl @@ -1,7 +1,8 @@ uniform mat4 mWorldViewProj; uniform mat4 mWorld; -uniform float dayNightRatio; +// Color of the light emitted by the sun. +uniform vec3 dayLight; uniform vec3 eyePosition; uniform float animationTimer; @@ -13,6 +14,8 @@ varying vec3 lightVec; varying vec3 tsEyeVec; varying vec3 tsLightVec; +// Color of the light emitted by the light sources. +const vec3 artificialLight = vec3(1.04, 1.04, 1.04); const float e = 2.718281828459; const float BS = 10.0; @@ -112,31 +115,23 @@ void main(void) eyeVec = (gl_ModelViewMatrix * gl_Vertex).xyz; tsEyeVec = eyeVec * tbnMatrix; + // Calculate color. + // Red, green and blue components are pre-multiplied with + // the brightness, so now we have to multiply these + // colors with the color of the incoming light. + // The pre-baked colors are halved to prevent overflow. vec4 color; - float day = gl_Color.r; - float night = gl_Color.g; - float light_source = gl_Color.b; - - float rg = mix(night, day, dayNightRatio); - rg += light_source * 2.5; // Make light sources brighter - float b = rg; - - // Moonlight is blue - b += (day - night) / 13.0; - rg -= (day - night) / 23.0; - + // The alpha gives the ratio of sunlight in the incoming light. + float nightRatio = 1 - gl_Color.a; + color.rgb = gl_Color.rgb * (gl_Color.a * dayLight.rgb + + nightRatio * artificialLight.rgb) * 2; + color.a = 1; + // Emphase blue a bit in darker places // See C++ implementation in mapblock_mesh.cpp finalColorBlend() - b += max(0.0, (1.0 - abs(b - 0.13)/0.17) * 0.025); - - // Artificial light is yellow-ish - // See C++ implementation in mapblock_mesh.cpp finalColorBlend() - rg += max(0.0, (1.0 - abs(rg - 0.85)/0.15) * 0.065); - - color.r = rg; - color.g = rg; - color.b = b; - - color.a = gl_Color.a; - gl_FrontColor = gl_BackColor = clamp(color,0.0,1.0); + float brightness = (color.r + color.g + color.b) / 3; + color.b += max(0.0, 0.021 - abs(0.2 * brightness - 0.021) + + 0.07 * brightness); + + gl_FrontColor = gl_BackColor = clamp(color, 0.0, 1.0); } diff --git a/client/shaders/wielded_shader/opengl_vertex.glsl b/client/shaders/wielded_shader/opengl_vertex.glsl index 86c626896..9f05b833a 100644 --- a/client/shaders/wielded_shader/opengl_vertex.glsl +++ b/client/shaders/wielded_shader/opengl_vertex.glsl @@ -1,7 +1,6 @@ uniform mat4 mWorldViewProj; uniform mat4 mWorld; -uniform float dayNightRatio; uniform vec3 eyePosition; uniform float animationTimer; diff --git a/doc/lua_api.txt b/doc/lua_api.txt index e5a3362ee..2a0b72053 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -638,6 +638,19 @@ node definition: bit 4 (0x10) - Makes the plant mesh 1.4x larger bit 5 (0x20) - Moves each face randomly a small bit down (1/8 max) bits 6-7 are reserved for future use. + paramtype2 == "color" + ^ `param2` tells which color is picked from the palette. + The palette should have 256 pixels. + paramtype2 == "colorfacedir" + ^ Same as `facedir`, but with colors. + The first three bits of `param2` tells which color + is picked from the palette. + The palette should have 8 pixels. + paramtype2 == "colorwallmounted" + ^ Same as `wallmounted`, but with colors. + The first five bits of `param2` tells which color + is picked from the palette. + The palette should have 32 pixels. collision_box = { type = "fixed", fixed = { @@ -3707,6 +3720,9 @@ Definition tables when displacement mapping is used Directions are from the point of view of the tile texture, not the node it's on +* `{name="image.png", color=ColorSpec}` + * the texture's color will be multiplied with this color. + * the tile's color overrides the owning node's color in all cases. * deprecated, yet still supported field names: * `image` (name) @@ -3749,8 +3765,17 @@ Definition tables special_tiles = {tile definition 1, Tile definition 2}, --[[ ^ Special textures of node; used rarely (old field name: special_materials) ^ List can be shortened to needed length ]] - alpha = 255, + color = ColorSpec, --[[ + ^ The node's original color will be multiplied with this color. + ^ If the node has a palette, then this setting only has an effect + ^ in the inventory and on the wield item. ]] use_texture_alpha = false, -- Use texture's alpha channel + palette = "palette.png", --[[ + ^ The node's `param2` is used to select a pixel from the image + ^ (pixels are arranged from left to right and from top to bottom). + ^ The node's color will be multiplied with the selected pixel's + ^ color. Tiles can override this behavior. + ^ Only when `paramtype2` supports palettes. ]] post_effect_color = "green#0F", -- If player is inside node, see "ColorSpec" paramtype = "none", -- See "Nodes" --[[ ^ paramtype = "light" allows light to propagate from or through the node with light value diff --git a/src/client/tile.cpp b/src/client/tile.cpp index 4d2166342..539c29445 100644 --- a/src/client/tile.cpp +++ b/src/client/tile.cpp @@ -378,9 +378,6 @@ public: video::ITexture* generateTextureFromMesh( const TextureFromMeshParams ¶ms); - // Generates an image from a full string like - // "stone.png^mineral_coal.png^[crack:1:0". - // Shall be called from the main thread. video::IImage* generateImage(const std::string &name); video::ITexture* getNormalTexture(const std::string &name); diff --git a/src/client/tile.h b/src/client/tile.h index 452804801..d04ab918a 100644 --- a/src/client/tile.h +++ b/src/client/tile.h @@ -108,6 +108,12 @@ public: const std::string &name, u32 *id = NULL) = 0; virtual IrrlichtDevice* getDevice()=0; virtual bool isKnownSourceImage(const std::string &name)=0; + /*! Generates an image from a full string like + * "stone.png^mineral_coal.png^[crack:1:0". + * Shall be called from the main thread. + * The returned Image should be dropped. + */ + virtual video::IImage* generateImage(const std::string &name)=0; virtual video::ITexture* generateTextureFromMesh( const TextureFromMeshParams ¶ms)=0; virtual video::ITexture* getNormalTexture(const std::string &name)=0; @@ -192,7 +198,6 @@ struct TileSpec texture(NULL), normal_texture(NULL), flags_texture(NULL), - alpha(255), material_type(TILE_MATERIAL_BASIC), material_flags( //0 // <- DEBUG, Use the one below @@ -201,22 +206,30 @@ struct TileSpec shader_id(0), animation_frame_count(1), animation_frame_length_ms(0), - rotation(0) + rotation(0), + has_color(false), + color(), + emissive_light(0) { } + /*! + * Two tiles are equal if they can be appended to + * the same mesh buffer. + */ bool operator==(const TileSpec &other) const { return ( texture_id == other.texture_id && - /* texture == other.texture && */ - alpha == other.alpha && material_type == other.material_type && material_flags == other.material_flags && rotation == other.rotation ); } + /*! + * Two tiles are not equal if they must be in different mesh buffers. + */ bool operator!=(const TileSpec &other) const { return !(*this == other); @@ -233,7 +246,7 @@ struct TileSpec material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; break; case TILE_MATERIAL_LIQUID_TRANSPARENT: - material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA; + material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; break; case TILE_MATERIAL_LIQUID_OPAQUE: material.MaterialType = video::EMT_SOLID; @@ -274,8 +287,6 @@ struct TileSpec video::ITexture *normal_texture; video::ITexture *flags_texture; - // Vertex alpha (when MATERIAL_ALPHA_VERTEX is used) - u8 alpha; // Material parameters u8 material_type; u8 material_flags; @@ -286,5 +297,14 @@ struct TileSpec std::vector frames; u8 rotation; + //! If true, the tile has its own color. + bool has_color; + /*! + * The color of the tile, or if the tile does not own + * a color then the color of the node owning this tile. + */ + video::SColor color; + //! This much light does the tile emit. + u8 emissive_light; }; #endif diff --git a/src/clientenvironment.cpp b/src/clientenvironment.cpp index b32a02f2d..77f53d214 100644 --- a/src/clientenvironment.cpp +++ b/src/clientenvironment.cpp @@ -334,9 +334,7 @@ void ClientEnvironment::step(float dtime) node_at_lplayer = m_map->getNodeNoEx(p); u16 light = getInteriorLight(node_at_lplayer, 0, m_client->ndef()); - u8 day = light & 0xff; - u8 night = (light >> 8) & 0xff; - finalColorBlend(lplayer->light_color, day, night, day_night_ratio); + final_color_blend(&lplayer->light_color, light, day_night_ratio); } /* diff --git a/src/content_mapblock.cpp b/src/content_mapblock.cpp index a7134590b..742bfb1fd 100644 --- a/src/content_mapblock.cpp +++ b/src/content_mapblock.cpp @@ -32,28 +32,29 @@ with this program; if not, write to the Free Software Foundation, Inc., // Create a cuboid. -// collector - the MeshCollector for the resulting polygons -// box - the position and size of the box -// tiles - the tiles (materials) to use (for all 6 faces) -// tilecount - number of entries in tiles, 1<=tilecount<=6 -// c - vertex colour - used for all -// txc - texture coordinates - this is a list of texture coordinates -// for the opposite corners of each face - therefore, there -// should be (2+2)*6=24 values in the list. Alternatively, pass -// NULL to use the entire texture for each face. The order of -// the faces in the list is up-down-right-left-back-front -// (compatible with ContentFeatures). If you specified 0,0,1,1 -// for each face, that would be the same as passing NULL. +// collector - the MeshCollector for the resulting polygons +// box - the position and size of the box +// tiles - the tiles (materials) to use (for all 6 faces) +// tilecount - number of entries in tiles, 1<=tilecount<=6 +// c - colors of the cuboid's six sides +// txc - texture coordinates - this is a list of texture coordinates +// for the opposite corners of each face - therefore, there +// should be (2+2)*6=24 values in the list. Alternatively, +// pass NULL to use the entire texture for each face. The +// order of the faces in the list is up-down-right-left-back- +// front (compatible with ContentFeatures). If you specified +// 0,0,1,1 for each face, that would be the same as +// passing NULL. +// light source - if greater than zero, the box's faces will not be shaded void makeCuboid(MeshCollector *collector, const aabb3f &box, - TileSpec *tiles, int tilecount, video::SColor &c, const f32* txc) + TileSpec *tiles, int tilecount, const video::SColor *c, + const f32* txc, const u8 light_source) { assert(tilecount >= 1 && tilecount <= 6); // pre-condition v3f min = box.MinEdge; v3f max = box.MaxEdge; - - if(txc == NULL) { static const f32 txc_default[24] = { 0,0,1,1, @@ -66,38 +67,53 @@ void makeCuboid(MeshCollector *collector, const aabb3f &box, txc = txc_default; } + video::SColor c1 = c[0]; + video::SColor c2 = c[1]; + video::SColor c3 = c[2]; + video::SColor c4 = c[3]; + video::SColor c5 = c[4]; + video::SColor c6 = c[5]; + if (!light_source) { + applyFacesShading(c1, v3f(0, 1, 0)); + applyFacesShading(c2, v3f(0, -1, 0)); + applyFacesShading(c3, v3f(1, 0, 0)); + applyFacesShading(c4, v3f(-1, 0, 0)); + applyFacesShading(c5, v3f(0, 0, 1)); + applyFacesShading(c6, v3f(0, 0, -1)); + } + video::S3DVertex vertices[24] = { // up - video::S3DVertex(min.X,max.Y,max.Z, 0,1,0, c, txc[0],txc[1]), - video::S3DVertex(max.X,max.Y,max.Z, 0,1,0, c, txc[2],txc[1]), - video::S3DVertex(max.X,max.Y,min.Z, 0,1,0, c, txc[2],txc[3]), - video::S3DVertex(min.X,max.Y,min.Z, 0,1,0, c, txc[0],txc[3]), + video::S3DVertex(min.X,max.Y,max.Z, 0,1,0, c1, txc[0],txc[1]), + video::S3DVertex(max.X,max.Y,max.Z, 0,1,0, c1, txc[2],txc[1]), + video::S3DVertex(max.X,max.Y,min.Z, 0,1,0, c1, txc[2],txc[3]), + video::S3DVertex(min.X,max.Y,min.Z, 0,1,0, c1, txc[0],txc[3]), // down - video::S3DVertex(min.X,min.Y,min.Z, 0,-1,0, c, txc[4],txc[5]), - video::S3DVertex(max.X,min.Y,min.Z, 0,-1,0, c, txc[6],txc[5]), - video::S3DVertex(max.X,min.Y,max.Z, 0,-1,0, c, txc[6],txc[7]), - video::S3DVertex(min.X,min.Y,max.Z, 0,-1,0, c, txc[4],txc[7]), + video::S3DVertex(min.X,min.Y,min.Z, 0,-1,0, c2, txc[4],txc[5]), + video::S3DVertex(max.X,min.Y,min.Z, 0,-1,0, c2, txc[6],txc[5]), + video::S3DVertex(max.X,min.Y,max.Z, 0,-1,0, c2, txc[6],txc[7]), + video::S3DVertex(min.X,min.Y,max.Z, 0,-1,0, c2, txc[4],txc[7]), // right - video::S3DVertex(max.X,max.Y,min.Z, 1,0,0, c, txc[ 8],txc[9]), - video::S3DVertex(max.X,max.Y,max.Z, 1,0,0, c, txc[10],txc[9]), - video::S3DVertex(max.X,min.Y,max.Z, 1,0,0, c, txc[10],txc[11]), - video::S3DVertex(max.X,min.Y,min.Z, 1,0,0, c, txc[ 8],txc[11]), + video::S3DVertex(max.X,max.Y,min.Z, 1,0,0, c3, txc[ 8],txc[9]), + video::S3DVertex(max.X,max.Y,max.Z, 1,0,0, c3, txc[10],txc[9]), + video::S3DVertex(max.X,min.Y,max.Z, 1,0,0, c3, txc[10],txc[11]), + video::S3DVertex(max.X,min.Y,min.Z, 1,0,0, c3, txc[ 8],txc[11]), // left - video::S3DVertex(min.X,max.Y,max.Z, -1,0,0, c, txc[12],txc[13]), - video::S3DVertex(min.X,max.Y,min.Z, -1,0,0, c, txc[14],txc[13]), - video::S3DVertex(min.X,min.Y,min.Z, -1,0,0, c, txc[14],txc[15]), - video::S3DVertex(min.X,min.Y,max.Z, -1,0,0, c, txc[12],txc[15]), + video::S3DVertex(min.X,max.Y,max.Z, -1,0,0, c4, txc[12],txc[13]), + video::S3DVertex(min.X,max.Y,min.Z, -1,0,0, c4, txc[14],txc[13]), + video::S3DVertex(min.X,min.Y,min.Z, -1,0,0, c4, txc[14],txc[15]), + video::S3DVertex(min.X,min.Y,max.Z, -1,0,0, c4, txc[12],txc[15]), // back - video::S3DVertex(max.X,max.Y,max.Z, 0,0,1, c, txc[16],txc[17]), - video::S3DVertex(min.X,max.Y,max.Z, 0,0,1, c, txc[18],txc[17]), - video::S3DVertex(min.X,min.Y,max.Z, 0,0,1, c, txc[18],txc[19]), - video::S3DVertex(max.X,min.Y,max.Z, 0,0,1, c, txc[16],txc[19]), + video::S3DVertex(max.X,max.Y,max.Z, 0,0,1, c5, txc[16],txc[17]), + video::S3DVertex(min.X,max.Y,max.Z, 0,0,1, c5, txc[18],txc[17]), + video::S3DVertex(min.X,min.Y,max.Z, 0,0,1, c5, txc[18],txc[19]), + video::S3DVertex(max.X,min.Y,max.Z, 0,0,1, c5, txc[16],txc[19]), // front - video::S3DVertex(min.X,max.Y,min.Z, 0,0,-1, c, txc[20],txc[21]), - video::S3DVertex(max.X,max.Y,min.Z, 0,0,-1, c, txc[22],txc[21]), - video::S3DVertex(max.X,min.Y,min.Z, 0,0,-1, c, txc[22],txc[23]), - video::S3DVertex(min.X,min.Y,min.Z, 0,0,-1, c, txc[20],txc[23]), + video::S3DVertex(min.X,max.Y,min.Z, 0,0,-1, c6, txc[20],txc[21]), + video::S3DVertex(max.X,max.Y,min.Z, 0,0,-1, c6, txc[22],txc[21]), + video::S3DVertex(max.X,min.Y,min.Z, 0,0,-1, c6, txc[22],txc[23]), + video::S3DVertex(min.X,min.Y,min.Z, 0,0,-1, c6, txc[20],txc[23]), }; for(int i = 0; i < 6; i++) @@ -164,6 +180,31 @@ void makeCuboid(MeshCollector *collector, const aabb3f &box, } } +// Create a cuboid. +// collector - the MeshCollector for the resulting polygons +// box - the position and size of the box +// tiles - the tiles (materials) to use (for all 6 faces) +// tilecount - number of entries in tiles, 1<=tilecount<=6 +// c - color of the cuboid +// txc - texture coordinates - this is a list of texture coordinates +// for the opposite corners of each face - therefore, there +// should be (2+2)*6=24 values in the list. Alternatively, +// pass NULL to use the entire texture for each face. The +// order of the faces in the list is up-down-right-left-back- +// front (compatible with ContentFeatures). If you specified +// 0,0,1,1 for each face, that would be the same as +// passing NULL. +// light source - if greater than zero, the box's faces will not be shaded +void makeCuboid(MeshCollector *collector, const aabb3f &box, TileSpec *tiles, + int tilecount, const video::SColor &c, const f32* txc, + const u8 light_source) +{ + video::SColor color[6]; + for (u8 i = 0; i < 6; i++) + color[i] = c; + makeCuboid(collector, box, tiles, tilecount, color, txc, light_source); +} + static inline void getNeighborConnectingFace(v3s16 p, INodeDefManager *nodedef, MeshMakeData *data, MapNode n, int v, int *neighbors) { @@ -181,6 +222,18 @@ static inline int NeighborToIndex(const v3s16 &pos) return 9 * pos.X + 3 * pos.Y + pos.Z + 13; } +/*! + * Returns the i-th special tile for a map node. + */ +static TileSpec getSpecialTile(const ContentFeatures &f, + const MapNode &n, u8 i) +{ + TileSpec copy = f.special_tiles[i]; + if (!copy.has_color) + n.getColor(f, ©.color); + return copy; +} + /* TODO: Fix alpha blending for special nodes Currently only the last element rendered is blended correct @@ -227,8 +280,13 @@ void mapblock_mesh_generate_special(MeshMakeData *data, /* Add water sources to mesh if using new style */ - TileSpec tile_liquid = f.special_tiles[0]; + TileSpec tile_liquid = getSpecialTile(f, n, 0); TileSpec tile_liquid_bfculled = getNodeTile(n, p, v3s16(0,0,0), data); + u16 l = getInteriorLight(n, 0, nodedef); + video::SColor c1 = encode_light_and_color(l, + tile_liquid.color, f.light_source); + video::SColor c2 = encode_light_and_color(l, + tile_liquid_bfculled.color, f.light_source); bool top_is_same_liquid = false; MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z)); @@ -237,9 +295,6 @@ void mapblock_mesh_generate_special(MeshMakeData *data, if(ntop.getContent() == c_flowing || ntop.getContent() == c_source) top_is_same_liquid = true; - u16 l = getInteriorLight(n, 0, nodedef); - video::SColor c = MapBlock_LightColor(f.alpha, l, f.light_source); - /* Generate sides */ @@ -285,15 +340,18 @@ void mapblock_mesh_generate_special(MeshMakeData *data, // Use backface culled material if neighbor doesn't have a // solidness of 0 const TileSpec *current_tile = &tile_liquid; - if(n_feat.solidness != 0 || n_feat.visual_solidness != 0) + video::SColor *c = &c1; + if(n_feat.solidness != 0 || n_feat.visual_solidness != 0) { current_tile = &tile_liquid_bfculled; + c = &c2; + } video::S3DVertex vertices[4] = { - video::S3DVertex(-BS/2,0,BS/2,0,0,0, c, 0,1), - video::S3DVertex(BS/2,0,BS/2,0,0,0, c, 1,1), - video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0), - video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0), + video::S3DVertex(-BS/2,0,BS/2,0,0,0, *c, 0,1), + video::S3DVertex(BS/2,0,BS/2,0,0,0, *c, 1,1), + video::S3DVertex(BS/2,0,BS/2, 0,0,0, *c, 1,0), + video::S3DVertex(-BS/2,0,BS/2, 0,0,0, *c, 0,0), }; /* @@ -359,10 +417,10 @@ void mapblock_mesh_generate_special(MeshMakeData *data, video::S3DVertex vertices[4] = { - video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1), - video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1), - video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,0), - video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,0), + video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c1, 0,1), + video::S3DVertex(BS/2,0,BS/2, 0,0,0, c1, 1,1), + video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c1, 1,0), + video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c1, 0,0), }; v3f offset(p.X * BS, (p.Y + 0.5) * BS, p.Z * BS); @@ -380,8 +438,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data, /* Add flowing liquid to mesh */ - TileSpec tile_liquid = f.special_tiles[0]; - TileSpec tile_liquid_bfculled = f.special_tiles[1]; + TileSpec tile_liquid = getSpecialTile(f, n, 0); + TileSpec tile_liquid_bfculled = getSpecialTile(f, n, 1); bool top_is_same_liquid = false; MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z)); @@ -404,7 +462,10 @@ void mapblock_mesh_generate_special(MeshMakeData *data, // Otherwise use the light of this node (the liquid) else l = getInteriorLight(n, 0, nodedef); - video::SColor c = MapBlock_LightColor(f.alpha, l, f.light_source); + video::SColor c1 = encode_light_and_color(l, + tile_liquid.color, f.light_source); + video::SColor c2 = encode_light_and_color(l, + tile_liquid_bfculled.color, f.light_source); u8 range = rangelim(nodedef->get(c_flowing).liquid_range, 1, 8); @@ -552,7 +613,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data, is liquid, don't draw side face */ if (top_is_same_liquid && - neighbor_data.flags & neighborflag_top_is_same_liquid) + (neighbor_data.flags & neighborflag_top_is_same_liquid)) continue; content_t neighbor_content = neighbor_data.content; @@ -574,15 +635,18 @@ void mapblock_mesh_generate_special(MeshMakeData *data, // Use backface culled material if neighbor doesn't have a // solidness of 0 const TileSpec *current_tile = &tile_liquid; - if(n_feat.solidness != 0 || n_feat.visual_solidness != 0) + video::SColor *c = &c1; + if(n_feat.solidness != 0 || n_feat.visual_solidness != 0) { current_tile = &tile_liquid_bfculled; + c = &c2; + } video::S3DVertex vertices[4] = { - video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1), - video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1), - video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0), - video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0), + video::S3DVertex(-BS/2,0,BS/2, 0,0,0, *c, 0,1), + video::S3DVertex(BS/2,0,BS/2, 0,0,0, *c, 1,1), + video::S3DVertex(BS/2,0,BS/2, 0,0,0, *c, 1,0), + video::S3DVertex(-BS/2,0,BS/2, 0,0,0, *c, 0,0), }; /* @@ -656,10 +720,10 @@ void mapblock_mesh_generate_special(MeshMakeData *data, { video::S3DVertex vertices[4] = { - video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1), - video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1), - video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,0), - video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,0), + video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c1, 0,1), + video::S3DVertex(BS/2,0,BS/2, 0,0,0, c1, 1,1), + video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c1, 1,0), + video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c1, 0,0), }; // To get backface culling right, the vertices need to go @@ -720,8 +784,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data, TileSpec tile = getNodeTile(n, p, v3s16(0,0,0), data); u16 l = getInteriorLight(n, 1, nodedef); - video::SColor c = MapBlock_LightColor(255, l, f.light_source); - + video::SColor c = encode_light_and_color(l, tile.color, + f.light_source); for(u32 j=0; j<6; j++) { // Check this neighbor @@ -731,13 +795,17 @@ void mapblock_mesh_generate_special(MeshMakeData *data, // Don't make face if neighbor is of same type if(n2.getContent() == n.getContent()) continue; + video::SColor c2=c; + if(!f.light_source) + applyFacesShading(c2, v3f(dir.X, dir.Y, dir.Z)); + // The face at Z+ video::S3DVertex vertices[4] = { - video::S3DVertex(-BS/2,-BS/2,BS/2, dir.X,dir.Y,dir.Z, c, 1,1), - video::S3DVertex(BS/2,-BS/2,BS/2, dir.X,dir.Y,dir.Z, c, 0,1), - video::S3DVertex(BS/2,BS/2,BS/2, dir.X,dir.Y,dir.Z, c, 0,0), - video::S3DVertex(-BS/2,BS/2,BS/2, dir.X,dir.Y,dir.Z, c, 1,0), + video::S3DVertex(-BS/2,-BS/2,BS/2, dir.X,dir.Y,dir.Z, c2, 1,1), + video::S3DVertex(BS/2,-BS/2,BS/2, dir.X,dir.Y,dir.Z, c2, 0,1), + video::S3DVertex(BS/2,BS/2,BS/2, dir.X,dir.Y,dir.Z, c2, 0,0), + video::S3DVertex(-BS/2,BS/2,BS/2, dir.X,dir.Y,dir.Z, c2, 1,0), }; // Rotations in the g_6dirs format @@ -784,12 +852,20 @@ void mapblock_mesh_generate_special(MeshMakeData *data, v3s16( 0, 0,-1) }; + u16 l = getInteriorLight(n, 1, nodedef); u8 i; TileSpec tiles[6]; for (i = 0; i < 6; i++) tiles[i] = getNodeTile(n, p, dirs[i], data); + video::SColor tile0color = encode_light_and_color(l, + tiles[0].color, f.light_source); + video::SColor tile0colors[6]; + for (i = 0; i < 6; i++) + tile0colors[i] = tile0color; + TileSpec glass_tiles[6]; + video::SColor glasscolor[6]; if (tiles[1].texture && tiles[2].texture && tiles[3].texture) { glass_tiles[0] = tiles[2]; glass_tiles[1] = tiles[3]; @@ -801,14 +877,15 @@ void mapblock_mesh_generate_special(MeshMakeData *data, for (i = 0; i < 6; i++) glass_tiles[i] = tiles[1]; } + for (i = 0; i < 6; i++) + glasscolor[i] = encode_light_and_color(l, glass_tiles[i].color, + f.light_source); u8 param2 = n.getParam2(); bool H_merge = ! bool(param2 & 128); bool V_merge = ! bool(param2 & 64); param2 = param2 & 63; - u16 l = getInteriorLight(n, 1, nodedef); - video::SColor c = MapBlock_LightColor(255, l, f.light_source); v3f pos = intToFloat(p, BS); static const float a = BS / 2; static const float g = a - 0.003; @@ -947,7 +1024,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data, 1-tx2, 1-ty2, 1-tx1, 1-ty1, tx1, 1-ty2, tx2, 1-ty1, }; - makeCuboid(&collector, box, &tiles[0], 1, c, txc1); + makeCuboid(&collector, box, &tiles[0], 1, tile0colors, + txc1, f.light_source); } for(i = 0; i < 6; i++) @@ -971,16 +1049,21 @@ void mapblock_mesh_generate_special(MeshMakeData *data, 1-tx2, 1-ty2, 1-tx1, 1-ty1, tx1, 1-ty2, tx2, 1-ty1, }; - makeCuboid(&collector, box, &glass_tiles[i], 1, c, txc2); + makeCuboid(&collector, box, &glass_tiles[i], 1, glasscolor, + txc2, f.light_source); } if (param2 > 0 && f.special_tiles[0].texture) { // Interior volume level is in range 0 .. 63, // convert it to -0.5 .. 0.5 float vlev = (((float)param2 / 63.0 ) * 2.0 - 1.0); + TileSpec tile=getSpecialTile(f, n, 0); + video::SColor special_color = encode_light_and_color(l, + tile.color, f.light_source); TileSpec interior_tiles[6]; for (i = 0; i < 6; i++) - interior_tiles[i] = f.special_tiles[0]; + interior_tiles[i] = tile; + float offset = 0.003; box = aabb3f(visible_faces[3] ? -b : -a + offset, visible_faces[1] ? -b : -a + offset, @@ -1004,22 +1087,24 @@ void mapblock_mesh_generate_special(MeshMakeData *data, 1-tx2, 1-ty2, 1-tx1, 1-ty1, tx1, 1-ty2, tx2, 1-ty1, }; - makeCuboid(&collector, box, interior_tiles, 6, c, txc3); + makeCuboid(&collector, box, interior_tiles, 6, special_color, + txc3, f.light_source); } break;} case NDT_ALLFACES: { TileSpec tile_leaves = getNodeTile(n, p, v3s16(0,0,0), data); - u16 l = getInteriorLight(n, 1, nodedef); - video::SColor c = MapBlock_LightColor(255, l, f.light_source); + video::SColor c = encode_light_and_color(l, + tile_leaves.color, f.light_source); v3f pos = intToFloat(p, BS); aabb3f box(-BS/2,-BS/2,-BS/2,BS/2,BS/2,BS/2); box.MinEdge += pos; box.MaxEdge += pos; - makeCuboid(&collector, box, &tile_leaves, 1, c, NULL); + makeCuboid(&collector, box, &tile_leaves, 1, c, NULL, + f.light_source); break;} case NDT_ALLFACES_OPTIONAL: // This is always pre-converted to something else @@ -1046,7 +1131,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data, tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY; u16 l = getInteriorLight(n, 1, nodedef); - video::SColor c = MapBlock_LightColor(255, l, f.light_source); + video::SColor c = encode_light_and_color(l, tile.color, + f.light_source); float s = BS/2*f.visual_scale; // Wall at X+ of node @@ -1087,7 +1173,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data, tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY; u16 l = getInteriorLight(n, 0, nodedef); - video::SColor c = MapBlock_LightColor(255, l, f.light_source); + video::SColor c = encode_light_and_color(l, tile.color, + f.light_source); float d = (float)BS/16; float s = BS/2*f.visual_scale; @@ -1132,7 +1219,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data, tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY; u16 l = getInteriorLight(n, 1, nodedef); - video::SColor c = MapBlock_LightColor(255, l, f.light_source); + video::SColor c = encode_light_and_color(l, tile.color, + f.light_source); float s = BS / 2 * f.visual_scale; // add sqrt(2) visual scale @@ -1302,7 +1390,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data, tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY; u16 l = getInteriorLight(n, 1, nodedef); - video::SColor c = MapBlock_LightColor(255, l, f.light_source); + video::SColor c = encode_light_and_color(l, tile.color, + f.light_source); float s = BS / 2 * f.visual_scale; @@ -1437,7 +1526,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data, tile_rot.rotation = 1; u16 l = getInteriorLight(n, 1, nodedef); - video::SColor c = MapBlock_LightColor(255, l, f.light_source); + video::SColor c = encode_light_and_color(l, tile.color, + f.light_source); const f32 post_rad=(f32)BS/8; const f32 bar_rad=(f32)BS/16; @@ -1456,7 +1546,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data, 4/16.,0,8/16.,1, 8/16.,0,12/16.,1, 12/16.,0,16/16.,1}; - makeCuboid(&collector, post, &tile_rot, 1, c, postuv); + makeCuboid(&collector, post, &tile_rot, 1, c, postuv, + f.light_source); // Now a section of fence, +X, if there's a post there v3s16 p2 = p; @@ -1477,11 +1568,11 @@ void mapblock_mesh_generate_special(MeshMakeData *data, 0/16.,8/16.,16/16.,10/16., 0/16.,14/16.,16/16.,16/16.}; makeCuboid(&collector, bar, &tile_nocrack, 1, - c, xrailuv); + c, xrailuv, f.light_source); bar.MinEdge.Y -= BS/2; bar.MaxEdge.Y -= BS/2; makeCuboid(&collector, bar, &tile_nocrack, 1, - c, xrailuv); + c, xrailuv, f.light_source); } // Now a section of fence, +Z, if there's a post there @@ -1503,11 +1594,11 @@ void mapblock_mesh_generate_special(MeshMakeData *data, 6/16.,6/16.,8/16.,8/16., 10/16.,10/16.,12/16.,12/16.}; makeCuboid(&collector, bar, &tile_nocrack, 1, - c, zrailuv); + c, zrailuv, f.light_source); bar.MinEdge.Y -= BS/2; bar.MaxEdge.Y -= BS/2; makeCuboid(&collector, bar, &tile_nocrack, 1, - c, zrailuv); + c, zrailuv, f.light_source); } break;} case NDT_RAILLIKE: @@ -1616,7 +1707,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data, tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY; u16 l = getInteriorLight(n, 0, nodedef); - video::SColor c = MapBlock_LightColor(255, l, f.light_source); + video::SColor c = encode_light_and_color(l, tile.color, + f.light_source); float d = (float)BS/64; float s = BS/2; @@ -1627,10 +1719,10 @@ void mapblock_mesh_generate_special(MeshMakeData *data, video::S3DVertex vertices[4] = { - video::S3DVertex(-s, -s+d,-s, 0,0,0, c,0,1), - video::S3DVertex( s, -s+d,-s, 0,0,0, c,1,1), - video::S3DVertex( s, g*s+d, s, 0,0,0, c,1,0), - video::S3DVertex(-s, g*s+d, s, 0,0,0, c,0,0), + video::S3DVertex(-s, -s+d, -s, 0, 0, 0, c, 0, 1), + video::S3DVertex( s, -s+d, -s, 0, 0, 0, c, 1, 1), + video::S3DVertex( s, g*s+d, s, 0, 0, 0, c, 1, 0), + video::S3DVertex(-s, g*s+d, s, 0, 0, 0, c, 0, 0), }; for(s32 i=0; i<4; i++) @@ -1653,10 +1745,16 @@ void mapblock_mesh_generate_special(MeshMakeData *data, v3s16(0, 0, 1), v3s16(0, 0, -1) }; - TileSpec tiles[6]; u16 l = getInteriorLight(n, 1, nodedef); - video::SColor c = MapBlock_LightColor(255, l, f.light_source); + TileSpec tiles[6]; + video::SColor colors[6]; + for(int j = 0; j < 6; j++) { + // Handles facedir rotation for textures + tiles[j] = getNodeTile(n, p, tile_dirs[j], data); + colors[j]= encode_light_and_color(l, tiles[j].color, + f.light_source); + } v3f pos = intToFloat(p, BS); @@ -1696,11 +1794,6 @@ void mapblock_mesh_generate_special(MeshMakeData *data, i = boxes.begin(); i != boxes.end(); ++i) { - for(int j = 0; j < 6; j++) - { - // Handles facedir rotation for textures - tiles[j] = getNodeTile(n, p, tile_dirs[j], data); - } aabb3f box = *i; box.MinEdge += pos; box.MaxEdge += pos; @@ -1747,18 +1840,19 @@ void mapblock_mesh_generate_special(MeshMakeData *data, // front tx1, 1-ty2, tx2, 1-ty1, }; - makeCuboid(&collector, box, tiles, 6, c, txc); + makeCuboid(&collector, box, tiles, 6, colors, txc, f.light_source); } break;} case NDT_MESH: { v3f pos = intToFloat(p, BS); - video::SColor c = MapBlock_LightColor(255, getInteriorLight(n, 1, nodedef), f.light_source); - + u16 l = getInteriorLight(n, 1, nodedef); u8 facedir = 0; - if (f.param_type_2 == CPT2_FACEDIR) { + if (f.param_type_2 == CPT2_FACEDIR || + f.param_type_2 == CPT2_COLORED_FACEDIR) { facedir = n.getFaceDir(nodedef); - } else if (f.param_type_2 == CPT2_WALLMOUNTED) { + } else if (f.param_type_2 == CPT2_WALLMOUNTED || + f.param_type_2 == CPT2_COLORED_WALLMOUNTED) { //convert wallmounted to 6dfacedir. //when cache enabled, it is already converted facedir = n.getWallMounted(nodedef); @@ -1771,10 +1865,13 @@ void mapblock_mesh_generate_special(MeshMakeData *data, if (f.mesh_ptr[facedir]) { // use cached meshes for(u16 j = 0; j < f.mesh_ptr[0]->getMeshBufferCount(); j++) { + const TileSpec &tile = getNodeTileN(n, p, j, data); scene::IMeshBuffer *buf = f.mesh_ptr[facedir]->getMeshBuffer(j); - collector.append(getNodeTileN(n, p, j, data), - (video::S3DVertex *)buf->getVertices(), buf->getVertexCount(), - buf->getIndices(), buf->getIndexCount(), pos, c); + collector.append(tile, (video::S3DVertex *) + buf->getVertices(), buf->getVertexCount(), + buf->getIndices(), buf->getIndexCount(), pos, + encode_light_and_color(l, tile.color, f.light_source), + f.light_source); } } else if (f.mesh_ptr[0]) { // no cache, clone and rotate mesh @@ -1783,10 +1880,13 @@ void mapblock_mesh_generate_special(MeshMakeData *data, recalculateBoundingBox(mesh); meshmanip->recalculateNormals(mesh, true, false); for(u16 j = 0; j < mesh->getMeshBufferCount(); j++) { + const TileSpec &tile = getNodeTileN(n, p, j, data); scene::IMeshBuffer *buf = mesh->getMeshBuffer(j); - collector.append(getNodeTileN(n, p, j, data), - (video::S3DVertex *)buf->getVertices(), buf->getVertexCount(), - buf->getIndices(), buf->getIndexCount(), pos, c); + collector.append(tile, (video::S3DVertex *) + buf->getVertices(), buf->getVertexCount(), + buf->getIndices(), buf->getIndexCount(), pos, + encode_light_and_color(l, tile.color, f.light_source), + f.light_source); } mesh->drop(); } diff --git a/src/game.cpp b/src/game.cpp index 1070cb1b2..07d429c6b 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -642,7 +642,7 @@ class GameGlobalShaderConstantSetter : public IShaderConstantSetter CachedPixelShaderSetting m_fog_distance; CachedVertexShaderSetting m_animation_timer_vertex; CachedPixelShaderSetting m_animation_timer_pixel; - CachedPixelShaderSetting m_day_night_ratio; + CachedPixelShaderSetting m_day_light; CachedPixelShaderSetting m_eye_position_pixel; CachedVertexShaderSetting m_eye_position_vertex; CachedPixelShaderSetting m_minimap_yaw; @@ -674,7 +674,7 @@ public: m_fog_distance("fogDistance"), m_animation_timer_vertex("animationTimer"), m_animation_timer_pixel("animationTimer"), - m_day_night_ratio("dayNightRatio"), + m_day_light("dayLight"), m_eye_position_pixel("eyePosition"), m_eye_position_vertex("eyePosition"), m_minimap_yaw("yawVec"), @@ -717,8 +717,14 @@ public: m_fog_distance.set(&fog_distance, services); - float daynight_ratio = (float)m_client->getEnv().getDayNightRatio() / 1000.f; - m_day_night_ratio.set(&daynight_ratio, services); + u32 daynight_ratio = (float)m_client->getEnv().getDayNightRatio(); + video::SColorf sunlight; + get_sunlight_color(&sunlight, daynight_ratio); + float dnc[3] = { + sunlight.r, + sunlight.g, + sunlight.b }; + m_day_light.set(dnc, services); u32 animation_timer = porting::getTimeMs() % 100000; float animation_timer_f = (float)animation_timer / 100000.f; @@ -840,7 +846,8 @@ bool nodePlacementPrediction(Client &client, // Predict param2 for facedir and wallmounted nodes u8 param2 = 0; - if (nodedef->get(id).param_type_2 == CPT2_WALLMOUNTED) { + if (nodedef->get(id).param_type_2 == CPT2_WALLMOUNTED || + nodedef->get(id).param_type_2 == CPT2_COLORED_WALLMOUNTED) { v3s16 dir = nodepos - neighbourpos; if (abs(dir.Y) > MYMAX(abs(dir.X), abs(dir.Z))) { @@ -852,7 +859,8 @@ bool nodePlacementPrediction(Client &client, } } - if (nodedef->get(id).param_type_2 == CPT2_FACEDIR) { + if (nodedef->get(id).param_type_2 == CPT2_FACEDIR || + nodedef->get(id).param_type_2 == CPT2_COLORED_FACEDIR) { v3s16 dir = nodepos - floatToInt(client.getEnv().getLocalPlayer()->getPosition(), BS); if (abs(dir.X) > abs(dir.Z)) { @@ -3749,11 +3757,9 @@ PointedThing Game::updatePointedThing( light_level = node_light; } - video::SColor c = MapBlock_LightColor(255, light_level, 0); - u8 day = c.getRed(); - u8 night = c.getGreen(); u32 daynight_ratio = client->getEnv().getDayNightRatio(); - finalColorBlend(c, day, night, daynight_ratio); + video::SColor c; + final_color_blend(&c, light_level, daynight_ratio); // Modify final color a bit with time u32 timer = porting::getTimeMs() % 5000; @@ -3964,7 +3970,7 @@ void Game::handleDigging(GameRunData *runData, const ContentFeatures &features = client->getNodeDefManager()->get(n); client->getParticleManager()->addPunchingParticles(client, smgr, - player, nodepos, features.tiles); + player, nodepos, n, features); } } @@ -4011,7 +4017,7 @@ void Game::handleDigging(GameRunData *runData, const ContentFeatures &features = client->getNodeDefManager()->get(wasnode); client->getParticleManager()->addDiggingParticles(client, smgr, - player, nodepos, features.tiles); + player, nodepos, wasnode, features); } runData->dig_time = 0; diff --git a/src/mapblock_mesh.cpp b/src/mapblock_mesh.cpp index 143adb410..dfd6f55a5 100644 --- a/src/mapblock_mesh.cpp +++ b/src/mapblock_mesh.cpp @@ -32,12 +32,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/directiontables.h" #include -static void applyFacesShading(video::SColor &color, const float factor) -{ - color.setRed(core::clamp(core::round32(color.getRed() * factor), 0, 255)); - color.setGreen(core::clamp(core::round32(color.getGreen() * factor), 0, 255)); -} - /* MeshMakeData */ @@ -321,19 +315,34 @@ u16 getSmoothLight(v3s16 p, v3s16 corner, MeshMakeData *data) return getSmoothLightCombined(p, data); } -/* - Converts from day + night color values (0..255) - and a given daynight_ratio to the final SColor shown on screen. -*/ -void finalColorBlend(video::SColor& result, - u8 day, u8 night, u32 daynight_ratio) +void get_sunlight_color(video::SColorf *sunlight, u32 daynight_ratio){ + f32 rg = daynight_ratio / 1000.0f - 0.04f; + f32 b = (0.98f * daynight_ratio) / 1000.0f + 0.078f; + sunlight->r = rg; + sunlight->g = rg; + sunlight->b = b; +} + +void final_color_blend(video::SColor *result, + u16 light, u32 daynight_ratio) { - s32 rg = (day * daynight_ratio + night * (1000-daynight_ratio)) / 1000; - s32 b = rg; + video::SColorf dayLight; + get_sunlight_color(&dayLight, daynight_ratio); + final_color_blend(result, + encode_light_and_color(light, video::SColor(0xFFFFFFFF), 0), dayLight); +} - // Moonlight is blue - b += (day - night) / 13; - rg -= (day - night) / 23; +void final_color_blend(video::SColor *result, + const video::SColor &data, const video::SColorf &dayLight) +{ + static const video::SColorf artificialColor(1.04f, 1.04f, 1.04f); + + video::SColorf c(data); + f32 n = 1 - c.a; + + f32 r = c.r * (c.a * dayLight.r + n * artificialColor.r) * 2.0f; + f32 g = c.g * (c.a * dayLight.g + n * artificialColor.g) * 2.0f; + f32 b = c.b * (c.a * dayLight.b + n * artificialColor.b) * 2.0f; // Emphase blue a bit in darker places // Each entry of this array represents a range of 8 blue levels @@ -341,19 +350,13 @@ void finalColorBlend(video::SColor& result, 1, 4, 6, 6, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; - b += emphase_blue_when_dark[irr::core::clamp(b, 0, 255) / 8]; - b = irr::core::clamp(b, 0, 255); - // Artificial light is yellow-ish - static const u8 emphase_yellow_when_artificial[16] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 10, 15, 15, 15 - }; - rg += emphase_yellow_when_artificial[night/16]; - rg = irr::core::clamp(rg, 0, 255); + b += emphase_blue_when_dark[irr::core::clamp((s32) ((r + g + b) / 3 * 255), + 0, 255) / 8] / 255.0f; - result.setRed(rg); - result.setGreen(rg); - result.setBlue(b); + result->setRed(core::clamp((s32) (r * 255.0f), 0, 255)); + result->setGreen(core::clamp((s32) (g * 255.0f), 0, 255)); + result->setBlue(core::clamp((s32) (b * 255.0f), 0, 255)); } /* @@ -430,7 +433,7 @@ struct FastFace }; static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3, - v3f p, v3s16 dir, v3f scale, u8 light_source, std::vector &dest) + v3f p, v3s16 dir, v3f scale, std::vector &dest) { // Position is at the center of the cube. v3f pos = p * BS; @@ -580,24 +583,25 @@ static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3, v3f normal(dir.X, dir.Y, dir.Z); - u8 alpha = tile.alpha; - dest.push_back(FastFace()); FastFace& face = *dest.rbegin(); - face.vertices[0] = video::S3DVertex(vertex_pos[0], normal, - MapBlock_LightColor(alpha, li0, light_source), - core::vector2d(x0+w*abs_scale, y0+h)); - face.vertices[1] = video::S3DVertex(vertex_pos[1], normal, - MapBlock_LightColor(alpha, li1, light_source), - core::vector2d(x0, y0+h)); - face.vertices[2] = video::S3DVertex(vertex_pos[2], normal, - MapBlock_LightColor(alpha, li2, light_source), - core::vector2d(x0, y0)); - face.vertices[3] = video::S3DVertex(vertex_pos[3], normal, - MapBlock_LightColor(alpha, li3, light_source), - core::vector2d(x0+w*abs_scale, y0)); + u16 li[4] = { li0, li1, li2, li3 }; + v2f32 f[4] = { + core::vector2d(x0 + w * abs_scale, y0 + h), + core::vector2d(x0, y0 + h), + core::vector2d(x0, y0), + core::vector2d(x0 + w * abs_scale, y0) }; + + for (u8 i = 0; i < 4; i++) { + video::SColor c = encode_light_and_color(li[i], tile.color, + tile.emissive_light); + if (!tile.emissive_light) + applyFacesShading(c, normal); + + face.vertices[i] = video::S3DVertex(vertex_pos[i], normal, c, f[i]); + } face.tile = tile; } @@ -664,7 +668,10 @@ static u8 face_contents(content_t m1, content_t m2, bool *equivalent, TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data) { INodeDefManager *ndef = data->m_client->ndef(); - TileSpec spec = ndef->get(mn).tiles[tileindex]; + const ContentFeatures &f = ndef->get(mn); + TileSpec spec = f.tiles[tileindex]; + if (!spec.has_color) + mn.getColor(f, &spec.color); // Apply temporary crack if (p == data->m_crack_pos_relative) spec.material_flags |= MATERIAL_FLAG_CRACK; @@ -747,8 +754,7 @@ static void getTileInfo( v3s16 &p_corrected, v3s16 &face_dir_corrected, u16 *lights, - TileSpec &tile, - u8 &light_source + TileSpec &tile ) { VoxelManipulator &vmanip = data->m_vmanip; @@ -763,7 +769,8 @@ static void getTileInfo( return; } - const MapNode &n1 = vmanip.getNodeRefUnsafeCheckFlags(blockpos_nodes + p + face_dir); + const MapNode &n1 = vmanip.getNodeRefUnsafeCheckFlags( + blockpos_nodes + p + face_dir); if (n1.getContent() == CONTENT_IGNORE) { makes_face = false; @@ -783,26 +790,25 @@ static void getTileInfo( makes_face = true; - if(mf == 1) - { - tile = getNodeTile(n0, p, face_dir, data); + MapNode n = n0; + + if (mf == 1) { p_corrected = p; face_dir_corrected = face_dir; - light_source = ndef->get(n0).light_source; - } - else - { - tile = getNodeTile(n1, p + face_dir, -face_dir, data); + } else { + n = n1; p_corrected = p + face_dir; face_dir_corrected = -face_dir; - light_source = ndef->get(n1).light_source; } + tile = getNodeTile(n, p_corrected, face_dir_corrected, data); + const ContentFeatures &f = ndef->get(n); + tile.emissive_light = f.light_source; // eg. water and glass - if(equivalent) + if (equivalent) tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING; - if(data->m_smooth_lighting == false) + if (data->m_smooth_lighting == false) { lights[0] = lights[1] = lights[2] = lights[3] = getFaceLight(n0, n1, face_dir, ndef); @@ -845,10 +851,9 @@ static void updateFastFaceRow( v3s16 face_dir_corrected; u16 lights[4] = {0,0,0,0}; TileSpec tile; - u8 light_source = 0; getTileInfo(data, p, face_dir, makes_face, p_corrected, face_dir_corrected, - lights, tile, light_source); + lights, tile); for(u16 j=0; javg("Meshgen: faces drawn by tiling", 0); for(int i = 1; i < continuous_tiles_count; i++){ @@ -958,7 +962,6 @@ static void updateFastFaceRow( lights[2] = next_lights[2]; lights[3] = next_lights[3]; tile = next_tile; - light_source = next_light_source; p = p_next; } } @@ -1083,12 +1086,14 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset): const u16 *indices_p = indices; /* - Revert triangles for nicer looking gradient if vertices - 1 and 3 have same color or 0 and 2 have different color. - getRed() is the day color. + Revert triangles for nicer looking gradient if the + brightness of vertices 1 and 3 differ less than + the brightness of vertices 0 and 2. */ - if(f.vertices[0].Color.getRed() != f.vertices[2].Color.getRed() - || f.vertices[1].Color.getRed() == f.vertices[3].Color.getRed()) + if (abs(f.vertices[0].Color.getAverage() + - f.vertices[2].Color.getAverage()) + > abs(f.vertices[1].Color.getAverage() + - f.vertices[3].Color.getAverage())) indices_p = indices_alternate; collector.append(f.tile, f.vertices, 4, indices_p, 6); @@ -1148,43 +1153,30 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset): p.tile.texture = animation_frame.texture; } - u32 vertex_count = m_use_tangent_vertices ? - p.tangent_vertices.size() : p.vertices.size(); - for (u32 j = 0; j < vertex_count; j++) { - v3f *Normal; - video::SColor *vc; - if (m_use_tangent_vertices) { - vc = &p.tangent_vertices[j].Color; - Normal = &p.tangent_vertices[j].Normal; - } else { - vc = &p.vertices[j].Color; - Normal = &p.vertices[j].Normal; - } - // Note applyFacesShading second parameter is precalculated sqrt - // value for speed improvement - // Skip it for lightsources and top faces. - if (!vc->getBlue()) { - if (Normal->Y < -0.5) { - applyFacesShading(*vc, 0.447213); - } else if (Normal->X > 0.5) { - applyFacesShading(*vc, 0.670820); - } else if (Normal->X < -0.5) { - applyFacesShading(*vc, 0.670820); - } else if (Normal->Z > 0.5) { - applyFacesShading(*vc, 0.836660); - } else if (Normal->Z < -0.5) { - applyFacesShading(*vc, 0.836660); - } - } - if (!m_enable_shaders) { - // - Classic lighting (shaders handle this by themselves) - // Set initial real color and store for later updates - u8 day = vc->getRed(); - u8 night = vc->getGreen(); - finalColorBlend(*vc, day, night, 1000); - if (day != night) { - m_daynight_diffs[i][j] = std::make_pair(day, night); + if (!m_enable_shaders) { + // Extract colors for day-night animation + // Dummy sunlight to handle non-sunlit areas + video::SColorf sunlight; + get_sunlight_color(&sunlight, 0); + u32 vertex_count = + m_use_tangent_vertices ? + p.tangent_vertices.size() : p.vertices.size(); + for (u32 j = 0; j < vertex_count; j++) { + video::SColor *vc; + if (m_use_tangent_vertices) { + vc = &p.tangent_vertices[j].Color; + } else { + vc = &p.vertices[j].Color; } + video::SColor copy(*vc); + if (vc->getAlpha() == 0) // No sunlight - no need to animate + final_color_blend(vc, copy, sunlight); // Finalize color + else // Record color to animate + m_daynight_diffs[i][j] = copy; + + // The sunlight ratio has been stored, + // delete alpha (for the final rendering). + vc->setAlpha(255); } } @@ -1358,19 +1350,19 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_rat if (m_enable_vbo) { m_mesh->setDirty(); } - for(std::map > >::iterator + video::SColorf day_color; + get_sunlight_color(&day_color, daynight_ratio); + for(std::map >::iterator i = m_daynight_diffs.begin(); i != m_daynight_diffs.end(); ++i) { scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first); video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices(); - for(std::map >::iterator + for(std::map::iterator j = i->second.begin(); j != i->second.end(); ++j) { - u8 day = j->second.first; - u8 night = j->second.second; - finalColorBlend(vertices[j->first].Color, day, night, daynight_ratio); + final_color_blend(&(vertices[j->first].Color), j->second, day_color); } } m_last_daynight_ratio = daynight_ratio; @@ -1452,7 +1444,7 @@ void MeshCollector::append(const TileSpec &tile, void MeshCollector::append(const TileSpec &tile, const video::S3DVertex *vertices, u32 numVertices, const u16 *indices, u32 numIndices, - v3f pos, video::SColor c) + v3f pos, video::SColor c, u8 light_source) { if (numIndices > 65535) { dstream<<"FIXME: MeshCollector::append() called with numIndices="<tangent_vertices.size(); for (u32 i = 0; i < numVertices; i++) { + if (!light_source) { + c = original_c; + applyFacesShading(c, vertices[i].Normal); + } video::S3DVertexTangents vert(vertices[i].Pos + pos, vertices[i].Normal, c, vertices[i].TCoords); p->tangent_vertices.push_back(vert); @@ -1489,8 +1486,12 @@ void MeshCollector::append(const TileSpec &tile, } else { vertex_count = p->vertices.size(); for (u32 i = 0; i < numVertices; i++) { - video::S3DVertex vert(vertices[i].Pos + pos, - vertices[i].Normal, c, vertices[i].TCoords); + if (!light_source) { + c = original_c; + applyFacesShading(c, vertices[i].Normal); + } + video::S3DVertex vert(vertices[i].Pos + pos, vertices[i].Normal, c, + vertices[i].TCoords); p->vertices.push_back(vert); } } @@ -1500,3 +1501,33 @@ void MeshCollector::append(const TileSpec &tile, p->indices.push_back(j); } } + +video::SColor encode_light_and_color(u16 light, const video::SColor &color, + u8 emissive_light) +{ + // Get components + f32 day = (light & 0xff) / 255.0f; + f32 night = (light >> 8) / 255.0f; + // Add emissive light + night += emissive_light * 0.01f; + if (night > 255) + night = 255; + // Since we don't know if the day light is sunlight or + // artificial light, assume it is artificial when the night + // light bank is also lit. + if (day < night) + day = 0; + else + day = day - night; + f32 sum = day + night; + // Ratio of sunlight: + float r; + if (sum > 0) + r = day / sum; + else + r = 0; + // Average light: + float b = (day + night) / 2; + return video::SColor(r * 255, b * color.getRed(), b * color.getGreen(), + b * color.getBlue()); +} diff --git a/src/mapblock_mesh.h b/src/mapblock_mesh.h index 5adb7df3f..916703f3e 100644 --- a/src/mapblock_mesh.h +++ b/src/mapblock_mesh.h @@ -156,8 +156,8 @@ private: // Animation info: day/night transitions // Last daynight_ratio value passed to animate() u32 m_last_daynight_ratio; - // For each meshbuffer, maps vertex indices to (day,night) pairs - std::map > > m_daynight_diffs; + // For each meshbuffer, stores pre-baked colors of sunlit vertices + std::map > m_daynight_diffs; // Camera offset info -> do we have to translate the mesh? v3s16 m_camera_offset; @@ -192,28 +192,53 @@ struct MeshCollector void append(const TileSpec &material, const video::S3DVertex *vertices, u32 numVertices, const u16 *indices, u32 numIndices, - v3f pos, video::SColor c); + v3f pos, video::SColor c, u8 light_source); }; -// This encodes -// alpha in the A channel of the returned SColor -// day light (0-255) in the R channel of the returned SColor -// night light (0-255) in the G channel of the returned SColor -// light source (0-255) in the B channel of the returned SColor -inline video::SColor MapBlock_LightColor(u8 alpha, u16 light, u8 light_source=0) -{ - return video::SColor(alpha, (light & 0xff), (light >> 8), light_source); -} +/*! + * Encodes light and color of a node. + * The result is not the final color, but a + * half-baked vertex color. + * + * \param light the first 8 bits are day light, + * the last 8 bits are night light + * \param color the node's color + * \param emissive_light amount of light the surface emits, + * from 0 to LIGHT_SUN. + */ +video::SColor encode_light_and_color(u16 light, const video::SColor &color, + u8 emissive_light); // Compute light at node u16 getInteriorLight(MapNode n, s32 increment, INodeDefManager *ndef); u16 getFaceLight(MapNode n, MapNode n2, v3s16 face_dir, INodeDefManager *ndef); u16 getSmoothLight(v3s16 p, v3s16 corner, MeshMakeData *data); -// Converts from day + night color values (0..255) -// and a given daynight_ratio to the final SColor shown on screen. -void finalColorBlend(video::SColor& result, - u8 day, u8 night, u32 daynight_ratio); +/*! + * Returns the sunlight's color from the current + * day-night ratio. + */ +void get_sunlight_color(video::SColorf *sunlight, u32 daynight_ratio); + +/*! + * Gives the final SColor shown on screen. + * + * \param result output color + * \param light first 8 bits are day light, second 8 bits are + * night light + */ +void final_color_blend(video::SColor *result, + u16 light, u32 daynight_ratio); + +/*! + * Gives the final SColor shown on screen. + * + * \param result output color + * \param data the half-baked vertex color + * \param dayLight color of the sunlight + */ +void final_color_blend(video::SColor *result, + const video::SColor &data, const video::SColorf &dayLight); // Retrieves the TileSpec of a face of a node // Adds MATERIAL_FLAG_CRACK if the node is cracked diff --git a/src/mapnode.cpp b/src/mapnode.cpp index f1a7f3e61..d835daba2 100644 --- a/src/mapnode.cpp +++ b/src/mapnode.cpp @@ -55,6 +55,15 @@ MapNode::MapNode(INodeDefManager *ndef, const std::string &name, param2 = a_param2; } +void MapNode::getColor(const ContentFeatures &f, video::SColor *color) const +{ + if (f.palette) { + *color = (*f.palette)[param2]; + return; + } + *color = f.color; +} + void MapNode::setLight(enum LightBank bank, u8 a_light, const ContentFeatures &f) { // If node doesn't contain light data, ignore this @@ -146,7 +155,8 @@ bool MapNode::getLightBanks(u8 &lightday, u8 &lightnight, INodeDefManager *nodem u8 MapNode::getFaceDir(INodeDefManager *nodemgr) const { const ContentFeatures &f = nodemgr->get(*this); - if(f.param_type_2 == CPT2_FACEDIR) + if (f.param_type_2 == CPT2_FACEDIR || + f.param_type_2 == CPT2_COLORED_FACEDIR) return (getParam2() & 0x1F) % 24; return 0; } @@ -154,7 +164,8 @@ u8 MapNode::getFaceDir(INodeDefManager *nodemgr) const u8 MapNode::getWallMounted(INodeDefManager *nodemgr) const { const ContentFeatures &f = nodemgr->get(*this); - if(f.param_type_2 == CPT2_WALLMOUNTED) + if (f.param_type_2 == CPT2_WALLMOUNTED || + f.param_type_2 == CPT2_COLORED_WALLMOUNTED) return getParam2() & 0x07; return 0; } @@ -176,7 +187,7 @@ void MapNode::rotateAlongYAxis(INodeDefManager *nodemgr, Rotation rot) { ContentParamType2 cpt2 = nodemgr->get(*this).param_type_2; - if (cpt2 == CPT2_FACEDIR) { + if (cpt2 == CPT2_FACEDIR || cpt2 == CPT2_COLORED_FACEDIR) { static const u8 rotate_facedir[24 * 4] = { // Table value = rotated facedir // Columns: 0, 90, 180, 270 degrees rotation around vertical axis @@ -216,7 +227,8 @@ void MapNode::rotateAlongYAxis(INodeDefManager *nodemgr, Rotation rot) u8 index = facedir * 4 + rot; param2 &= ~31; param2 |= rotate_facedir[index]; - } else if (cpt2 == CPT2_WALLMOUNTED) { + } else if (cpt2 == CPT2_WALLMOUNTED || + cpt2 == CPT2_COLORED_WALLMOUNTED) { u8 wmountface = (param2 & 7); if (wmountface <= 1) return; diff --git a/src/mapnode.h b/src/mapnode.h index ae0245cfe..9c56a7e17 100644 --- a/src/mapnode.h +++ b/src/mapnode.h @@ -20,9 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #ifndef MAPNODE_HEADER #define MAPNODE_HEADER -#include "irrlichttypes.h" -#include "irr_v3d.h" -#include "irr_aabb3d.h" +#include "irrlichttypes_bloated.h" #include "light.h" #include #include @@ -187,6 +185,14 @@ struct MapNode param2 = p; } + /*! + * Returns the color of the node. + * + * \param f content features of this node + * \param color output, contains the node's color. + */ + void getColor(const ContentFeatures &f, video::SColor *color) const; + void setLight(enum LightBank bank, u8 a_light, const ContentFeatures &f); void setLight(enum LightBank bank, u8 a_light, INodeDefManager *nodemgr); diff --git a/src/mesh.cpp b/src/mesh.cpp index 50465748c..84689b631 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -33,13 +33,25 @@ with this program; if not, write to the Free Software Foundation, Inc., #define MY_ETLM_READ_ONLY video::ETLM_READ_ONLY #endif -static void applyFacesShading(video::SColor& color, float factor) +inline static void applyShadeFactor(video::SColor& color, float factor) { color.setRed(core::clamp(core::round32(color.getRed()*factor), 0, 255)); color.setGreen(core::clamp(core::round32(color.getGreen()*factor), 0, 255)); color.setBlue(core::clamp(core::round32(color.getBlue()*factor), 0, 255)); } +void applyFacesShading(video::SColor &color, const v3f &normal) +{ + // Many special drawtypes have normals set to 0,0,0 and this + // must result in maximum brightness (no face shadng). + if (normal.Y < -0.5f) + applyShadeFactor (color, 0.447213f); + else if (normal.X > 0.5f || normal.X < -0.5f) + applyShadeFactor (color, 0.670820f); + else if (normal.Z > 0.5f || normal.Z < -0.5f) + applyShadeFactor (color, 0.836660f); +} + scene::IAnimatedMesh* createCubeMesh(v3f scale) { video::SColor c(255,255,255,255); @@ -172,29 +184,18 @@ void setMeshColor(scene::IMesh *mesh, const video::SColor &color) } } -void shadeMeshFaces(scene::IMesh *mesh) +void colorizeMeshBuffer(scene::IMeshBuffer *buf, const video::SColor *buffercolor) { - if (mesh == NULL) - return; - - u32 mc = mesh->getMeshBufferCount(); - for (u32 j = 0; j < mc; j++) { - scene::IMeshBuffer *buf = mesh->getMeshBuffer(j); - const u32 stride = getVertexPitchFromType(buf->getVertexType()); - u32 vertex_count = buf->getVertexCount(); - u8 *vertices = (u8 *)buf->getVertices(); - for (u32 i = 0; i < vertex_count; i++) { - video::S3DVertex *vertex = (video::S3DVertex *)(vertices + i * stride); - video::SColor &vc = vertex->Color; - // Many special drawtypes have normals set to 0,0,0 and this - // must result in maximum brightness (no face shadng). - if (vertex->Normal.Y < -0.5f) - applyFacesShading (vc, 0.447213f); - else if (vertex->Normal.X > 0.5f || vertex->Normal.X < -0.5f) - applyFacesShading (vc, 0.670820f); - else if (vertex->Normal.Z > 0.5f || vertex->Normal.Z < -0.5f) - applyFacesShading (vc, 0.836660f); - } + const u32 stride = getVertexPitchFromType(buf->getVertexType()); + u32 vertex_count = buf->getVertexCount(); + u8 *vertices = (u8 *) buf->getVertices(); + for (u32 i = 0; i < vertex_count; i++) { + video::S3DVertex *vertex = (video::S3DVertex *) (vertices + i * stride); + video::SColor *vc = &(vertex->Color); + // Reset color + *vc = *buffercolor; + // Apply shading + applyFacesShading(*vc, vertex->Normal); } } diff --git a/src/mesh.h b/src/mesh.h index 10df97015..bcf0d771c 100644 --- a/src/mesh.h +++ b/src/mesh.h @@ -23,6 +23,12 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irrlichttypes_extrabloated.h" #include "nodedef.h" +/*! + * Applies shading to a color based on the surface's + * normal vector. + */ +void applyFacesShading(video::SColor &color, const v3f &normal); + /* Create a new cube mesh. Vertices are at (+-scale.X/2, +-scale.Y/2, +-scale.Z/2). @@ -48,11 +54,7 @@ void translateMesh(scene::IMesh *mesh, v3f vec); */ void setMeshColor(scene::IMesh *mesh, const video::SColor &color); -/* - Shade mesh faces according to their normals -*/ - -void shadeMeshFaces(scene::IMesh *mesh); +void colorizeMeshBuffer(scene::IMeshBuffer *buf, const video::SColor *buffercolor); /* Set the color of all vertices in the mesh. @@ -87,7 +89,7 @@ void rotateMeshYZby (scene::IMesh *mesh, f64 degrees); scene::IMesh* cloneMesh(scene::IMesh *src_mesh); /* - Convert nodeboxes to mesh. + Convert nodeboxes to mesh. Each tile goes into a different buffer. boxes - set of nodeboxes to be converted into cuboids uv_coords[24] - table of texture uv coords for each cuboid face expand - factor by which cuboids will be resized diff --git a/src/minimap.cpp b/src/minimap.cpp index f49adb517..a2e501751 100644 --- a/src/minimap.cpp +++ b/src/minimap.cpp @@ -144,7 +144,7 @@ MinimapPixel *MinimapUpdateThread::getMinimapPixel(v3s16 pos, if (it != m_blocks_cache.end()) { MinimapMapblock *mmblock = it->second; MinimapPixel *pixel = &mmblock->data[relpos.Z * MAP_BLOCKSIZE + relpos.X]; - if (pixel->id != CONTENT_AIR) { + if (pixel->n.param0 != CONTENT_AIR) { *pixel_height = height + pixel->height; return pixel; } @@ -187,7 +187,7 @@ void MinimapUpdateThread::getMap(v3s16 pos, s16 size, s16 height, bool is_radar) for (s16 x = 0; x < size; x++) for (s16 z = 0; z < size; z++) { - u16 id = CONTENT_AIR; + MapNode n(CONTENT_AIR); MinimapPixel *mmpixel = &data->minimap_scan[x + z * size]; if (!is_radar) { @@ -195,14 +195,14 @@ void MinimapUpdateThread::getMap(v3s16 pos, s16 size, s16 height, bool is_radar) MinimapPixel *cached_pixel = getMinimapPixel(v3s16(p.X + x, p.Y, p.Z + z), height, &pixel_height); if (cached_pixel) { - id = cached_pixel->id; + n = cached_pixel->n; mmpixel->height = pixel_height; } } else { mmpixel->air_count = getAirCount(v3s16(p.X + x, p.Y, p.Z + z), height); } - mmpixel->id = id; + mmpixel->n = n; } } @@ -372,10 +372,21 @@ void Mapper::blitMinimapPixelsToImageSurface( for (s16 z = 0; z < data->map_size; z++) { MinimapPixel *mmpixel = &data->minimap_scan[x + z * data->map_size]; - video::SColor c = m_ndef->get(mmpixel->id).minimap_color; - c.setAlpha(240); - - map_image->setPixel(x, data->map_size - z - 1, c); + const ContentFeatures &f = m_ndef->get(mmpixel->n); + const TileDef *tile = &f.tiledef[0]; + // Color of the 0th tile (mostly this is the topmost) + video::SColor tilecolor; + if(tile->has_color) + tilecolor = tile->color; + else + mmpixel->n.getColor(f, &tilecolor); + tilecolor.setRed(tilecolor.getRed() * f.minimap_color.getRed() / 255); + tilecolor.setGreen(tilecolor.getGreen() * f.minimap_color.getGreen() + / 255); + tilecolor.setBlue(tilecolor.getBlue() * f.minimap_color.getBlue() / 255); + tilecolor.setAlpha(240); + + map_image->setPixel(x, data->map_size - z - 1, tilecolor); u32 h = mmpixel->height; heightmap_image->setPixel(x,data->map_size - z - 1, @@ -617,7 +628,7 @@ void MinimapMapblock::getMinimapNodes(VoxelManipulator *vmanip, v3s16 pos) MapNode n = vmanip->getNodeNoEx(pos + p); if (!surface_found && n.getContent() != CONTENT_AIR) { mmpixel->height = y; - mmpixel->id = n.getContent(); + mmpixel->n = n; surface_found = true; } else if (n.getContent() == CONTENT_AIR) { air_count++; @@ -625,7 +636,7 @@ void MinimapMapblock::getMinimapNodes(VoxelManipulator *vmanip, v3s16 pos) } if (!surface_found) - mmpixel->id = CONTENT_AIR; + mmpixel->n = MapNode(CONTENT_AIR); mmpixel->air_count = air_count; } diff --git a/src/minimap.h b/src/minimap.h index 743b2bff2..81ed0e49f 100644 --- a/src/minimap.h +++ b/src/minimap.h @@ -52,10 +52,10 @@ struct MinimapModeDef { }; struct MinimapPixel { - u16 id; + //! The topmost node that the minimap displays. + MapNode n; u16 height; u16 air_count; - u16 light; }; struct MinimapMapblock { diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index 23c8a665b..a511d169b 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -143,9 +143,12 @@ with this program; if not, write to the Free Software Foundation, Inc., serialization of TileAnimation params changed TAT_SHEET_2D Removed client-sided chat perdiction + PROTOCOL VERSION 30: + New ContentFeatures serialization version + Add node and tile color and palette */ -#define LATEST_PROTOCOL_VERSION 29 +#define LATEST_PROTOCOL_VERSION 30 // Server's supported network protocol range #define SERVER_PROTOCOL_VERSION_MIN 13 diff --git a/src/nodedef.cpp b/src/nodedef.cpp index a4af26e87..98f795c7a 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -189,7 +189,9 @@ void NodeBox::deSerialize(std::istream &is) void TileDef::serialize(std::ostream &os, u16 protocol_version) const { - if (protocol_version >= 29) + if (protocol_version >= 30) + writeU8(os, 4); + else if (protocol_version >= 29) writeU8(os, 3); else if (protocol_version >= 26) writeU8(os, 2); @@ -205,6 +207,14 @@ void TileDef::serialize(std::ostream &os, u16 protocol_version) const writeU8(os, tileable_horizontal); writeU8(os, tileable_vertical); } + if (protocol_version >= 30) { + writeU8(os, has_color); + if (has_color) { + writeU8(os, color.getRed()); + writeU8(os, color.getGreen()); + writeU8(os, color.getBlue()); + } + } } void TileDef::deSerialize(std::istream &is, const u8 contenfeatures_version, const NodeDrawType drawtype) @@ -218,6 +228,14 @@ void TileDef::deSerialize(std::istream &is, const u8 contenfeatures_version, con tileable_horizontal = readU8(is); tileable_vertical = readU8(is); } + if (version >= 4) { + has_color = readU8(is); + if (has_color) { + color.setRed(readU8(is)); + color.setGreen(readU8(is)); + color.setBlue(readU8(is)); + } + } if ((contenfeatures_version < 8) && ((drawtype == NDT_MESH) || @@ -351,172 +369,222 @@ void ContentFeatures::reset() connects_to.clear(); connects_to_ids.clear(); connect_sides = 0; + color = video::SColor(0xFFFFFFFF); + palette_name = ""; + palette = NULL; } void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const { - if(protocol_version < 24){ + if (protocol_version < 30) { serializeOld(os, protocol_version); return; } - writeU8(os, protocol_version < 27 ? 7 : 8); + // version + writeU8(os, 9); - os<first); + for (ItemGroupList::const_iterator i = groups.begin(); i != groups.end(); + ++i) { + os << serializeString(i->first); writeS16(os, i->second); } + writeU8(os, param_type); + writeU8(os, param_type_2); + + // visual writeU8(os, drawtype); + os << serializeString(mesh); writeF1000(os, visual_scale); writeU8(os, 6); - for(u32 i = 0; i < 6; i++) + for (u32 i = 0; i < 6; i++) tiledef[i].serialize(os, protocol_version); writeU8(os, CF_SPECIAL_COUNT); - for(u32 i = 0; i < CF_SPECIAL_COUNT; i++){ + for (u32 i = 0; i < CF_SPECIAL_COUNT; i++) { tiledef_special[i].serialize(os, protocol_version); } writeU8(os, alpha); + writeU8(os, color.getRed()); + writeU8(os, color.getGreen()); + writeU8(os, color.getBlue()); + os << serializeString(palette_name); + writeU8(os, waving); + writeU8(os, connect_sides); + writeU16(os, connects_to_ids.size()); + for (std::set::const_iterator i = connects_to_ids.begin(); + i != connects_to_ids.end(); ++i) + writeU16(os, *i); writeU8(os, post_effect_color.getAlpha()); writeU8(os, post_effect_color.getRed()); writeU8(os, post_effect_color.getGreen()); writeU8(os, post_effect_color.getBlue()); - writeU8(os, param_type); - if ((protocol_version < 28) && (param_type_2 == CPT2_MESHOPTIONS)) - writeU8(os, CPT2_NONE); - else - writeU8(os, param_type_2); - writeU8(os, is_ground_content); + writeU8(os, leveled); + + // lighting writeU8(os, light_propagates); writeU8(os, sunlight_propagates); + writeU8(os, light_source); + + // map generation + writeU8(os, is_ground_content); + + // interaction writeU8(os, walkable); writeU8(os, pointable); writeU8(os, diggable); writeU8(os, climbable); writeU8(os, buildable_to); - os<::const_iterator i = connects_to_ids.begin(); - i != connects_to_ids.end(); ++i) - writeU16(os, *i); - writeU8(os, connect_sides); + + // legacy + writeU8(os, legacy_facedir_simple); + writeU8(os, legacy_wallmounted); +} + +void ContentFeatures::correctAlpha() +{ + if (alpha == 0 || alpha == 255) + return; + + for (u32 i = 0; i < 6; i++) { + std::stringstream s; + s << tiledef[i].name << "^[noalpha^[opacity:" << ((int)alpha); + tiledef[i].name = s.str(); + } + + for (u32 i = 0; i < CF_SPECIAL_COUNT; i++) { + std::stringstream s; + s << tiledef_special[i].name << "^[noalpha^[opacity:" << ((int)alpha); + tiledef_special[i].name = s.str(); + } } void ContentFeatures::deSerialize(std::istream &is) { + // version detection int version = readU8(is); - if (version < 7) { + if (version < 9) { deSerializeOld(is, version); return; - } else if (version > 8) { + } else if (version > 9) { throw SerializationError("unsupported ContentFeatures version"); } + // general name = deSerializeString(is); groups.clear(); u32 groups_size = readU16(is); - for(u32 i = 0; i < groups_size; i++){ + for (u32 i = 0; i < groups_size; i++) { std::string name = deSerializeString(is); int value = readS16(is); groups[name] = value; } - drawtype = (enum NodeDrawType)readU8(is); + param_type = (enum ContentParamType) readU8(is); + param_type_2 = (enum ContentParamType2) readU8(is); + // visual + drawtype = (enum NodeDrawType) readU8(is); + mesh = deSerializeString(is); visual_scale = readF1000(is); - if(readU8(is) != 6) + if (readU8(is) != 6) throw SerializationError("unsupported tile count"); - for(u32 i = 0; i < 6; i++) + for (u32 i = 0; i < 6; i++) tiledef[i].deSerialize(is, version, drawtype); - if(readU8(is) != CF_SPECIAL_COUNT) + if (readU8(is) != CF_SPECIAL_COUNT) throw SerializationError("unsupported CF_SPECIAL_COUNT"); - for(u32 i = 0; i < CF_SPECIAL_COUNT; i++) + for (u32 i = 0; i < CF_SPECIAL_COUNT; i++) tiledef_special[i].deSerialize(is, version, drawtype); alpha = readU8(is); + color.setRed(readU8(is)); + color.setGreen(readU8(is)); + color.setBlue(readU8(is)); + palette_name = deSerializeString(is); + waving = readU8(is); + connect_sides = readU8(is); + u16 connects_to_size = readU16(is); + connects_to_ids.clear(); + for (u16 i = 0; i < connects_to_size; i++) + connects_to_ids.insert(readU16(is)); post_effect_color.setAlpha(readU8(is)); post_effect_color.setRed(readU8(is)); post_effect_color.setGreen(readU8(is)); post_effect_color.setBlue(readU8(is)); - param_type = (enum ContentParamType)readU8(is); - param_type_2 = (enum ContentParamType2)readU8(is); - is_ground_content = readU8(is); + leveled = readU8(is); + + // lighting-related light_propagates = readU8(is); sunlight_propagates = readU8(is); + light_source = readU8(is); + light_source = MYMIN(light_source, LIGHT_MAX); + + // map generation + is_ground_content = readU8(is); + + // interaction walkable = readU8(is); pointable = readU8(is); diggable = readU8(is); climbable = readU8(is); buildable_to = readU8(is); - deSerializeString(is); // legacy: used to be metadata_name - liquid_type = (enum LiquidType)readU8(is); + rightclickable = readU8(is); + damage_per_second = readU32(is); + + // liquid + liquid_type = (enum LiquidType) readU8(is); liquid_alternative_flowing = deSerializeString(is); liquid_alternative_source = deSerializeString(is); liquid_viscosity = readU8(is); liquid_renewable = readU8(is); - light_source = readU8(is); - light_source = MYMIN(light_source, LIGHT_MAX); - damage_per_second = readU32(is); + liquid_range = readU8(is); + drowning = readU8(is); + floodable = readU8(is); + + // node boxes node_box.deSerialize(is); selection_box.deSerialize(is); - legacy_facedir_simple = readU8(is); - legacy_wallmounted = readU8(is); + collision_box.deSerialize(is); + + // sounds deSerializeSimpleSoundSpec(sound_footstep, is); deSerializeSimpleSoundSpec(sound_dig, is); deSerializeSimpleSoundSpec(sound_dug, is); - rightclickable = readU8(is); - drowning = readU8(is); - leveled = readU8(is); - liquid_range = readU8(is); - waving = readU8(is); - // If you add anything here, insert it primarily inside the try-catch - // block to not need to increase the version. - try{ - // Stuff below should be moved to correct place in a version that - // otherwise changes the protocol version - mesh = deSerializeString(is); - collision_box.deSerialize(is); - floodable = readU8(is); - u16 connects_to_size = readU16(is); - connects_to_ids.clear(); - for (u16 i = 0; i < connects_to_size; i++) - connects_to_ids.insert(readU16(is)); - connect_sides = readU8(is); - }catch(SerializationError &e) {}; + + // read legacy properties + legacy_facedir_simple = readU8(is); + legacy_wallmounted = readU8(is); } #ifndef SERVER void ContentFeatures::fillTileAttribs(ITextureSource *tsrc, TileSpec *tile, TileDef *tiledef, u32 shader_id, bool use_normal_texture, - bool backface_culling, u8 alpha, u8 material_type) + bool backface_culling, u8 material_type) { tile->shader_id = shader_id; tile->texture = tsrc->getTextureForMesh(tiledef->name, &tile->texture_id); - tile->alpha = alpha; tile->material_type = material_type; // Normal texture and shader flags texture @@ -536,6 +604,13 @@ void ContentFeatures::fillTileAttribs(ITextureSource *tsrc, TileSpec *tile, if (tiledef->tileable_vertical) tile->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL; + // Color + tile->has_color = tiledef->has_color; + if (tiledef->has_color) + tile->color = tiledef->color; + else + tile->color = color; + // Animation parameters int frame_count = 1; if (tile->material_flags & MATERIAL_FLAG_ANIMATION) { @@ -681,6 +756,9 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc is_water_surface = true; } + // Vertex alpha is no longer supported, correct if necessary. + correctAlpha(); + u32 tile_shader[6]; for (u16 j = 0; j < 6; j++) { tile_shader[j] = shdsrc->getShader("nodes_shader", @@ -696,14 +774,14 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc for (u16 j = 0; j < 6; j++) { fillTileAttribs(tsrc, &tiles[j], &tdef[j], tile_shader[j], tsettings.use_normal_texture, - tiledef[j].backface_culling, alpha, material_type); + tiledef[j].backface_culling, material_type); } // Special tiles (fill in f->special_tiles[]) for (u16 j = 0; j < CF_SPECIAL_COUNT; j++) { fillTileAttribs(tsrc, &special_tiles[j], &tiledef_special[j], tile_shader[j], tsettings.use_normal_texture, - tiledef_special[j].backface_culling, alpha, material_type); + tiledef_special[j].backface_culling, material_type); } if ((drawtype == NDT_MESH) && (mesh != "")) { @@ -731,15 +809,19 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc } //Cache 6dfacedir and wallmounted rotated clones of meshes - if (tsettings.enable_mesh_cache && mesh_ptr[0] && (param_type_2 == CPT2_FACEDIR)) { + if (tsettings.enable_mesh_cache && mesh_ptr[0] && + (param_type_2 == CPT2_FACEDIR + || param_type_2 == CPT2_COLORED_FACEDIR)) { for (u16 j = 1; j < 24; j++) { mesh_ptr[j] = cloneMesh(mesh_ptr[0]); rotateMeshBy6dFacedir(mesh_ptr[j], j); recalculateBoundingBox(mesh_ptr[j]); meshmanip->recalculateNormals(mesh_ptr[j], true, false); } - } else if (tsettings.enable_mesh_cache && mesh_ptr[0] && (param_type_2 == CPT2_WALLMOUNTED)) { - static const u8 wm_to_6d[6] = {20, 0, 16+1, 12+3, 8, 4+2}; + } else if (tsettings.enable_mesh_cache && mesh_ptr[0] + && (param_type_2 == CPT2_WALLMOUNTED || + param_type_2 == CPT2_COLORED_WALLMOUNTED)) { + static const u8 wm_to_6d[6] = { 20, 0, 16 + 1, 12 + 3, 8, 4 + 2 }; for (u16 j = 1; j < 6; j++) { mesh_ptr[j] = cloneMesh(mesh_ptr[0]); rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]); @@ -775,6 +857,9 @@ public: virtual void removeNode(const std::string &name); virtual void updateAliases(IItemDefManager *idef); virtual void applyTextureOverrides(const std::string &override_filepath); + //! Returns a palette or NULL if not found. Only on client. + std::vector *getPalette(const ContentFeatures &f, + const IGameDef *gamedef); virtual void updateTextures(IGameDef *gamedef, void (*progress_cbk)(void *progress_args, u32 progress, u32 max_progress), void *progress_cbk_args); @@ -823,6 +908,9 @@ private: // Next possibly free id content_t m_next_id; + // Maps image file names to loaded palettes. + UNORDERED_MAP > m_palettes; + // NodeResolvers to callback once node registration has ended std::vector m_pending_resolve_callbacks; @@ -1062,7 +1150,8 @@ void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features, if (nodebox.type == NODEBOX_LEVELED) { half_processed.MaxEdge.Y = +BS / 2; } - if (features.param_type_2 == CPT2_FACEDIR) { + if (features.param_type_2 == CPT2_FACEDIR || + features.param_type_2 == CPT2_COLORED_FACEDIR) { // Get maximal coordinate f32 coords[] = { fabsf(half_processed.MinEdge.X), @@ -1309,6 +1398,78 @@ void CNodeDefManager::applyTextureOverrides(const std::string &override_filepath } } +std::vector *CNodeDefManager::getPalette( + const ContentFeatures &f, const IGameDef *gamedef) +{ +#ifndef SERVER + // This works because colors always use the most significant bits + // of param2. If you add a new colored type which uses param2 + // in a more advanced way, you should change this code, too. + u32 palette_pixels = 0; + switch (f.param_type_2) { + case CPT2_COLOR: + palette_pixels = 256; + break; + case CPT2_COLORED_FACEDIR: + palette_pixels = 8; + break; + case CPT2_COLORED_WALLMOUNTED: + palette_pixels = 32; + break; + default: + return NULL; + } + // This many param2 values will have the same color + u32 step = 256 / palette_pixels; + const std::string &name = f.palette_name; + if (name == "") + return NULL; + Client *client = (Client *) gamedef; + ITextureSource *tsrc = client->tsrc(); + + UNORDERED_MAP >::iterator it = + m_palettes.find(name); + if (it == m_palettes.end()) { + // Create palette + if (!tsrc->isKnownSourceImage(name)) { + warningstream << "CNodeDefManager::getPalette(): palette \"" << name + << "\" could not be loaded." << std::endl; + return NULL; + } + video::IImage *img = tsrc->generateImage(name); + std::vector new_palette; + u32 w = img->getDimension().Width; + u32 h = img->getDimension().Height; + // Real area of the image + u32 area = h * w; + if (area != palette_pixels) + warningstream << "CNodeDefManager::getPalette(): the " + << "specified palette image \"" << name << "\" does not " + << "contain exactly " << palette_pixels + << " pixels." << std::endl; + if (area > palette_pixels) + area = palette_pixels; + // For each pixel in the image + for (u32 i = 0; i < area; i++) { + video::SColor c = img->getPixel(i % w, i / w); + // Fill in palette with 'step' colors + for (u32 j = 0; j < step; j++) + new_palette.push_back(c); + } + img->drop(); + // Fill in remaining elements + while (new_palette.size() < 256) + new_palette.push_back(video::SColor(0xFFFFFFFF)); + m_palettes[name] = new_palette; + it = m_palettes.find(name); + } + if (it != m_palettes.end()) + return &((*it).second); + +#endif + return NULL; +} + void CNodeDefManager::updateTextures(IGameDef *gamedef, void (*progress_callback)(void *progress_args, u32 progress, u32 max_progress), void *progress_callback_args) @@ -1325,10 +1486,13 @@ void CNodeDefManager::updateTextures(IGameDef *gamedef, TextureSettings tsettings; tsettings.readSettings(); + m_palettes.clear(); u32 size = m_content_features.size(); for (u32 i = 0; i < size; i++) { - m_content_features[i].updateTextures(tsrc, shdsrc, meshmanip, client, tsettings); + ContentFeatures *f = &(m_content_features[i]); + f->palette = getPalette(*f, gamedef); + f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings); progress_callback(progress_callback_args, i, size); } #endif @@ -1429,6 +1593,19 @@ IWritableNodeDefManager *createNodeDefManager() //// Serialization of old ContentFeatures formats void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const { + u8 compatible_param_type_2 = param_type_2; + if ((protocol_version < 28) + && (compatible_param_type_2 == CPT2_MESHOPTIONS)) + compatible_param_type_2 = CPT2_NONE; + else if (protocol_version < 30) { + if (compatible_param_type_2 == CPT2_COLOR) + compatible_param_type_2 = CPT2_NONE; + else if (compatible_param_type_2 == CPT2_COLORED_FACEDIR) + compatible_param_type_2 = CPT2_FACEDIR; + else if (compatible_param_type_2 == CPT2_COLORED_WALLMOUNTED) + compatible_param_type_2 = CPT2_WALLMOUNTED; + } + if (protocol_version == 13) { writeU8(os, 5); // version @@ -1454,7 +1631,7 @@ void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const writeU8(os, post_effect_color.getGreen()); writeU8(os, post_effect_color.getBlue()); writeU8(os, param_type); - writeU8(os, param_type_2); + writeU8(os, compatible_param_type_2); writeU8(os, is_ground_content); writeU8(os, light_propagates); writeU8(os, sunlight_propagates); @@ -1483,9 +1660,9 @@ void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const os<first); - writeS16(os, i->second); + i = groups.begin(); i != groups.end(); ++i) { + os<first); + writeS16(os, i->second); } writeU8(os, drawtype); writeF1000(os, visual_scale); @@ -1502,7 +1679,7 @@ void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const writeU8(os, post_effect_color.getGreen()); writeU8(os, post_effect_color.getBlue()); writeU8(os, param_type); - writeU8(os, param_type_2); + writeU8(os, compatible_param_type_2); writeU8(os, is_ground_content); writeU8(os, light_propagates); writeU8(os, sunlight_propagates); @@ -1530,6 +1707,68 @@ void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const writeU8(os, drowning); writeU8(os, leveled); writeU8(os, liquid_range); + } + else if(protocol_version >= 24 && protocol_version < 30) { + writeU8(os, protocol_version < 27 ? 7 : 8); + + os << serializeString(name); + writeU16(os, groups.size()); + for (ItemGroupList::const_iterator i = groups.begin(); + i != groups.end(); ++i) { + os << serializeString(i->first); + writeS16(os, i->second); + } + writeU8(os, drawtype); + writeF1000(os, visual_scale); + writeU8(os, 6); + for (u32 i = 0; i < 6; i++) + tiledef[i].serialize(os, protocol_version); + writeU8(os, CF_SPECIAL_COUNT); + for (u32 i = 0; i < CF_SPECIAL_COUNT; i++) + tiledef_special[i].serialize(os, protocol_version); + writeU8(os, alpha); + writeU8(os, post_effect_color.getAlpha()); + writeU8(os, post_effect_color.getRed()); + writeU8(os, post_effect_color.getGreen()); + writeU8(os, post_effect_color.getBlue()); + writeU8(os, param_type); + writeU8(os, compatible_param_type_2); + writeU8(os, is_ground_content); + writeU8(os, light_propagates); + writeU8(os, sunlight_propagates); + writeU8(os, walkable); + writeU8(os, pointable); + writeU8(os, diggable); + writeU8(os, climbable); + writeU8(os, buildable_to); + os << serializeString(""); // legacy: used to be metadata_name + writeU8(os, liquid_type); + os << serializeString(liquid_alternative_flowing); + os << serializeString(liquid_alternative_source); + writeU8(os, liquid_viscosity); + writeU8(os, liquid_renewable); + writeU8(os, light_source); + writeU32(os, damage_per_second); + node_box.serialize(os, protocol_version); + selection_box.serialize(os, protocol_version); + writeU8(os, legacy_facedir_simple); + writeU8(os, legacy_wallmounted); + serializeSimpleSoundSpec(sound_footstep, os); + serializeSimpleSoundSpec(sound_dig, os); + serializeSimpleSoundSpec(sound_dug, os); + writeU8(os, rightclickable); + writeU8(os, drowning); + writeU8(os, leveled); + writeU8(os, liquid_range); + writeU8(os, waving); + os << serializeString(mesh); + collision_box.serialize(os, protocol_version); + writeU8(os, floodable); + writeU16(os, connects_to_ids.size()); + for (std::set::const_iterator i = connects_to_ids.begin(); + i != connects_to_ids.end(); ++i) + writeU16(os, *i); + writeU8(os, connect_sides); } else throw SerializationError("ContentFeatures::serialize(): " "Unsupported version requested"); @@ -1642,7 +1881,73 @@ void ContentFeatures::deSerializeOld(std::istream &is, int version) drowning = readU8(is); leveled = readU8(is); liquid_range = readU8(is); - } else { + } else if (version == 7 || version == 8){ + name = deSerializeString(is); + groups.clear(); + u32 groups_size = readU16(is); + for (u32 i = 0; i < groups_size; i++) { + std::string name = deSerializeString(is); + int value = readS16(is); + groups[name] = value; + } + drawtype = (enum NodeDrawType) readU8(is); + + visual_scale = readF1000(is); + if (readU8(is) != 6) + throw SerializationError("unsupported tile count"); + for (u32 i = 0; i < 6; i++) + tiledef[i].deSerialize(is, version, drawtype); + if (readU8(is) != CF_SPECIAL_COUNT) + throw SerializationError("unsupported CF_SPECIAL_COUNT"); + for (u32 i = 0; i < CF_SPECIAL_COUNT; i++) + tiledef_special[i].deSerialize(is, version, drawtype); + alpha = readU8(is); + post_effect_color.setAlpha(readU8(is)); + post_effect_color.setRed(readU8(is)); + post_effect_color.setGreen(readU8(is)); + post_effect_color.setBlue(readU8(is)); + param_type = (enum ContentParamType) readU8(is); + param_type_2 = (enum ContentParamType2) readU8(is); + is_ground_content = readU8(is); + light_propagates = readU8(is); + sunlight_propagates = readU8(is); + walkable = readU8(is); + pointable = readU8(is); + diggable = readU8(is); + climbable = readU8(is); + buildable_to = readU8(is); + deSerializeString(is); // legacy: used to be metadata_name + liquid_type = (enum LiquidType) readU8(is); + liquid_alternative_flowing = deSerializeString(is); + liquid_alternative_source = deSerializeString(is); + liquid_viscosity = readU8(is); + liquid_renewable = readU8(is); + light_source = readU8(is); + light_source = MYMIN(light_source, LIGHT_MAX); + damage_per_second = readU32(is); + node_box.deSerialize(is); + selection_box.deSerialize(is); + legacy_facedir_simple = readU8(is); + legacy_wallmounted = readU8(is); + deSerializeSimpleSoundSpec(sound_footstep, is); + deSerializeSimpleSoundSpec(sound_dig, is); + deSerializeSimpleSoundSpec(sound_dug, is); + rightclickable = readU8(is); + drowning = readU8(is); + leveled = readU8(is); + liquid_range = readU8(is); + waving = readU8(is); + try { + mesh = deSerializeString(is); + collision_box.deSerialize(is); + floodable = readU8(is); + u16 connects_to_size = readU16(is); + connects_to_ids.clear(); + for (u16 i = 0; i < connects_to_size; i++) + connects_to_ids.insert(readU16(is)); + connect_sides = readU8(is); + } catch (SerializationError &e) {}; + }else{ throw SerializationError("unsupported ContentFeatures version"); } } @@ -1736,19 +2041,23 @@ bool CNodeDefManager::nodeboxConnects(MapNode from, MapNode to, u8 connect_face) // does to node declare usable faces? if (f2.connect_sides > 0) { - if ((f2.param_type_2 == CPT2_FACEDIR) && (connect_face >= 4)) { - static const u8 rot[33 * 4] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 - back - 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 - right - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 16, 8, 4, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - front - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 32, 16, 8, 4 // 32 - left - }; - return (f2.connect_sides & rot[(connect_face * 4) + to.param2]); + if ((f2.param_type_2 == CPT2_FACEDIR || + f2.param_type_2 == CPT2_COLORED_FACEDIR) + && (connect_face >= 4)) { + static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, // 4 - back + 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, // 8 - right + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, // 16 - front + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 32, 16, 8, 4 // 32 - left + }; + return (f2.connect_sides + & rot[(connect_face * 4) + (to.param2 & 0x1F)]); } return (f2.connect_sides & connect_face); } diff --git a/src/nodedef.h b/src/nodedef.h index 183b95d87..6275b41ce 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -68,7 +68,13 @@ enum ContentParamType2 // 2D rotation for things like plants CPT2_DEGROTATE, // Mesh options for plants - CPT2_MESHOPTIONS + CPT2_MESHOPTIONS, + // Index for palette + CPT2_COLOR, + // 3 bits of palette index, then facedir + CPT2_COLORED_FACEDIR, + // 5 bits of palette index, then wallmounted + CPT2_COLORED_WALLMOUNTED }; enum LiquidType @@ -170,6 +176,11 @@ struct TileDef bool backface_culling; // Takes effect only in special cases bool tileable_horizontal; bool tileable_vertical; + //! If true, the tile has its own color. + bool has_color; + //! The color of the tile. + video::SColor color; + struct TileAnimationParams animation; TileDef() @@ -178,6 +189,8 @@ struct TileDef backface_culling = true; tileable_horizontal = true; tileable_vertical = true; + has_color = false; + color = video::SColor(0xFFFFFFFF); animation.type = TAT_NONE; } @@ -191,7 +204,7 @@ struct ContentFeatures { /* Cached stuff - */ + */ #ifndef SERVER // 0 1 2 3 4 5 // up down right left back front @@ -211,12 +224,19 @@ struct ContentFeatures /* Actual data - */ + */ + + // --- GENERAL PROPERTIES --- std::string name; // "" = undefined node ItemGroupList groups; // Same as in itemdef + // Type of MapNode::param1 + ContentParamType param_type; + // Type of MapNode::param2 + ContentParamType2 param_type_2; + + // --- VISUAL PROPERTIES --- - // Visual definition enum NodeDrawType drawtype; std::string mesh; #ifndef SERVER @@ -226,19 +246,38 @@ struct ContentFeatures float visual_scale; // Misc. scale parameter TileDef tiledef[6]; TileDef tiledef_special[CF_SPECIAL_COUNT]; // eg. flowing liquid + // If 255, the node is opaque. + // Otherwise it uses texture alpha. u8 alpha; - + // The color of the node. + video::SColor color; + std::string palette_name; + std::vector *palette; + // Used for waving leaves/plants + u8 waving; + // for NDT_CONNECTED pairing + u8 connect_sides; + std::vector connects_to; + std::set connects_to_ids; // Post effect color, drawn when the camera is inside the node. video::SColor post_effect_color; + // Flowing liquid or snow, value = default level + u8 leveled; + + // --- LIGHTING-RELATED --- - // Type of MapNode::param1 - ContentParamType param_type; - // Type of MapNode::param2 - ContentParamType2 param_type_2; - // True for all ground-like things like stone and mud, false for eg. trees - bool is_ground_content; bool light_propagates; bool sunlight_propagates; + // Amount of light the node emits + u8 light_source; + + // --- MAP GENERATION --- + + // True for all ground-like things like stone and mud, false for eg. trees + bool is_ground_content; + + // --- INTERACTION PROPERTIES --- + // This is used for collision detection. // Also for general solidness queries. bool walkable; @@ -250,12 +289,12 @@ struct ContentFeatures bool climbable; // Player can build on these bool buildable_to; - // Liquids flow into and replace node - bool floodable; // Player cannot build to these (placement prediction disabled) bool rightclickable; - // Flowing liquid or snow, value = default level - u8 leveled; + u32 damage_per_second; + + // --- LIQUID PROPERTIES --- + // Whether the node is non-liquid, source liquid or flowing liquid enum LiquidType liquid_type; // If the content is liquid, this is the flowing version of the liquid. @@ -271,29 +310,28 @@ struct ContentFeatures // Number of flowing liquids surrounding source u8 liquid_range; u8 drowning; - // Amount of light the node emits - u8 light_source; - u32 damage_per_second; + // Liquids flow into and replace node + bool floodable; + + // --- NODEBOXES --- + NodeBox node_box; NodeBox selection_box; NodeBox collision_box; - // Used for waving leaves/plants - u8 waving; - // Compatibility with old maps - // Set to true if paramtype used to be 'facedir_simple' - bool legacy_facedir_simple; - // Set to true if wall_mounted used to be set to true - bool legacy_wallmounted; - // for NDT_CONNECTED pairing - u8 connect_sides; - // Sound properties + // --- SOUND PROPERTIES --- + SimpleSoundSpec sound_footstep; SimpleSoundSpec sound_dig; SimpleSoundSpec sound_dug; - std::vector connects_to; - std::set connects_to_ids; + // --- LEGACY --- + + // Compatibility with old maps + // Set to true if paramtype used to be 'facedir_simple' + bool legacy_facedir_simple; + // Set to true if wall_mounted used to be set to true + bool legacy_wallmounted; /* Methods @@ -306,6 +344,14 @@ struct ContentFeatures void deSerialize(std::istream &is); void serializeOld(std::ostream &os, u16 protocol_version) const; void deSerializeOld(std::istream &is, int version); + /*! + * Since vertex alpha is no lnger supported, this method + * adds instructions to the texture names to blend alpha there. + * + * tiledef, tiledef_special and alpha must be initialized + * before calling this. + */ + void correctAlpha(); /* Some handy methods @@ -321,7 +367,7 @@ struct ContentFeatures #ifndef SERVER void fillTileAttribs(ITextureSource *tsrc, TileSpec *tile, TileDef *tiledef, u32 shader_id, bool use_normal_texture, bool backface_culling, - u8 alpha, u8 material_type); + u8 material_type); void updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc, scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings); #endif diff --git a/src/particles.cpp b/src/particles.cpp index 5f17763e0..e1f292fb6 100644 --- a/src/particles.cpp +++ b/src/particles.cpp @@ -56,7 +56,8 @@ Particle::Particle( v2f texpos, v2f texsize, const struct TileAnimationParams &anim, - u8 glow + u8 glow, + video::SColor color ): scene::ISceneNode(smgr->getRootSceneNode(), smgr) { @@ -77,6 +78,10 @@ Particle::Particle( m_animation_frame = 0; m_animation_time = 0.0; + // Color + m_base_color = color; + m_color = color; + // Particle related m_pos = pos; m_velocity = velocity; @@ -183,12 +188,15 @@ void Particle::updateLight() else light = blend_light(m_env->getDayNightRatio(), LIGHT_SUN, 0); - m_light = decode_light(light + m_glow); + u8 m_light = decode_light(light + m_glow); + m_color.set(255, + m_light * m_base_color.getRed() / 255, + m_light * m_base_color.getGreen() / 255, + m_light * m_base_color.getBlue() / 255); } void Particle::updateVertices() { - video::SColor c(255, m_light, m_light, m_light); f32 tx0, tx1, ty0, ty1; if (m_animation.type != TAT_NONE) { @@ -210,14 +218,14 @@ void Particle::updateVertices() ty1 = m_texpos.Y + m_texsize.Y; } - m_vertices[0] = video::S3DVertex(-m_size/2,-m_size/2,0, 0,0,0, - c, tx0, ty1); - m_vertices[1] = video::S3DVertex(m_size/2,-m_size/2,0, 0,0,0, - c, tx1, ty1); - m_vertices[2] = video::S3DVertex(m_size/2,m_size/2,0, 0,0,0, - c, tx1, ty0); - m_vertices[3] = video::S3DVertex(-m_size/2,m_size/2,0, 0,0,0, - c, tx0, ty0); + m_vertices[0] = video::S3DVertex(-m_size / 2, -m_size / 2, + 0, 0, 0, 0, m_color, tx0, ty1); + m_vertices[1] = video::S3DVertex(m_size / 2, -m_size / 2, + 0, 0, 0, 0, m_color, tx1, ty1); + m_vertices[2] = video::S3DVertex(m_size / 2, m_size / 2, + 0, 0, 0, 0, m_color, tx1, ty0); + m_vertices[3] = video::S3DVertex(-m_size / 2, m_size / 2, + 0, 0, 0, 0, m_color, tx0, ty0); v3s16 camera_offset = m_env->getCameraOffset(); for(u16 i=0; i<4; i++) @@ -589,35 +597,39 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, Client *client, } } -void ParticleManager::addDiggingParticles(IGameDef* gamedef, scene::ISceneManager* smgr, - LocalPlayer *player, v3s16 pos, const TileSpec tiles[]) +void ParticleManager::addDiggingParticles(IGameDef* gamedef, + scene::ISceneManager* smgr, LocalPlayer *player, v3s16 pos, + const MapNode &n, const ContentFeatures &f) { for (u16 j = 0; j < 32; j++) // set the amount of particles here { - addNodeParticle(gamedef, smgr, player, pos, tiles); + addNodeParticle(gamedef, smgr, player, pos, n, f); } } -void ParticleManager::addPunchingParticles(IGameDef* gamedef, scene::ISceneManager* smgr, - LocalPlayer *player, v3s16 pos, const TileSpec tiles[]) +void ParticleManager::addPunchingParticles(IGameDef* gamedef, + scene::ISceneManager* smgr, LocalPlayer *player, v3s16 pos, + const MapNode &n, const ContentFeatures &f) { - addNodeParticle(gamedef, smgr, player, pos, tiles); + addNodeParticle(gamedef, smgr, player, pos, n, f); } -void ParticleManager::addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr, - LocalPlayer *player, v3s16 pos, const TileSpec tiles[]) +void ParticleManager::addNodeParticle(IGameDef* gamedef, + scene::ISceneManager* smgr, LocalPlayer *player, v3s16 pos, + const MapNode &n, const ContentFeatures &f) { // Texture u8 texid = myrand_range(0, 5); + const TileSpec &tile = f.tiles[texid]; video::ITexture *texture; struct TileAnimationParams anim; anim.type = TAT_NONE; // Only use first frame of animated texture - if (tiles[texid].material_flags & MATERIAL_FLAG_ANIMATION) - texture = tiles[texid].frames[0].texture; + if (tile.material_flags & MATERIAL_FLAG_ANIMATION) + texture = tile.frames[0].texture; else - texture = tiles[texid].texture; + texture = tile.texture; float size = rand() % 64 / 512.; float visual_size = BS * size; @@ -638,6 +650,12 @@ void ParticleManager::addNodeParticle(IGameDef* gamedef, scene::ISceneManager* s (f32) pos.Z + rand() %100 /200. - 0.25 ); + video::SColor color; + if (tile.has_color) + color = tile.color; + else + n.getColor(f, &color); + Particle* toadd = new Particle( gamedef, smgr, @@ -655,7 +673,8 @@ void ParticleManager::addNodeParticle(IGameDef* gamedef, scene::ISceneManager* s texpos, texsize, anim, - 0); + 0, + color); addParticle(toadd); } diff --git a/src/particles.h b/src/particles.h index 5464e6672..3177f2cfd 100644 --- a/src/particles.h +++ b/src/particles.h @@ -32,6 +32,8 @@ with this program; if not, write to the Free Software Foundation, Inc., struct ClientEvent; class ParticleManager; class ClientEnvironment; +class MapNode; +class ContentFeatures; class Particle : public scene::ISceneNode { @@ -53,7 +55,8 @@ class Particle : public scene::ISceneNode v2f texpos, v2f texsize, const struct TileAnimationParams &anim, - u8 glow + u8 glow, + video::SColor color = video::SColor(0xFFFFFFFF) ); ~Particle(); @@ -100,7 +103,10 @@ private: v3f m_acceleration; LocalPlayer *m_player; float m_size; - u8 m_light; + //! Color without lighting + video::SColor m_base_color; + //! Final rendered color + video::SColor m_color; bool m_collisiondetection; bool m_collision_removal; bool m_vertical; @@ -184,13 +190,16 @@ public: scene::ISceneManager* smgr, LocalPlayer *player); void addDiggingParticles(IGameDef* gamedef, scene::ISceneManager* smgr, - LocalPlayer *player, v3s16 pos, const TileSpec tiles[]); + LocalPlayer *player, v3s16 pos, const MapNode &n, + const ContentFeatures &f); void addPunchingParticles(IGameDef* gamedef, scene::ISceneManager* smgr, - LocalPlayer *player, v3s16 pos, const TileSpec tiles[]); + LocalPlayer *player, v3s16 pos, const MapNode &n, + const ContentFeatures &f); void addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr, - LocalPlayer *player, v3s16 pos, const TileSpec tiles[]); + LocalPlayer *player, v3s16 pos, const MapNode &n, + const ContentFeatures &f); protected: void addParticle(Particle* toadd); diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 84af4583b..ebc951295 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -332,6 +332,10 @@ TileDef read_tiledef(lua_State *L, int index, u8 drawtype) L, index, "tileable_horizontal", default_tiling); tiledef.tileable_vertical = getboolfield_default( L, index, "tileable_vertical", default_tiling); + // color = ... + lua_getfield(L, index, "color"); + tiledef.has_color = read_color(L, -1, &tiledef.color); + lua_pop(L, 1); // animation = {} lua_getfield(L, index, "animation"); tiledef.animation = read_animation_definition(L, -1); @@ -450,6 +454,13 @@ ContentFeatures read_content_features(lua_State *L, int index) if (usealpha) f.alpha = 0; + // Read node color. + lua_getfield(L, index, "color"); + read_color(L, -1, &f.color); + lua_pop(L, 1); + + getstringfield(L, index, "palette", f.palette_name); + /* Other stuff */ lua_getfield(L, index, "post_effect_color"); @@ -461,6 +472,13 @@ ContentFeatures read_content_features(lua_State *L, int index) f.param_type_2 = (ContentParamType2)getenumfield(L, index, "paramtype2", ScriptApiNode::es_ContentParamType2, CPT2_NONE); + if (f.palette_name != "" && + !(f.param_type_2 == CPT2_COLOR || + f.param_type_2 == CPT2_COLORED_FACEDIR || + f.param_type_2 == CPT2_COLORED_WALLMOUNTED)) + warningstream << "Node " << f.name.c_str() + << " has a palette, but not a suitable paramtype2." << std::endl; + // Warn about some deprecated fields warn_if_field_exists(L, index, "wall_mounted", "Deprecated; use paramtype2 = 'wallmounted'"); diff --git a/src/script/cpp_api/s_node.cpp b/src/script/cpp_api/s_node.cpp index 379ed773f..23c8f43b9 100644 --- a/src/script/cpp_api/s_node.cpp +++ b/src/script/cpp_api/s_node.cpp @@ -59,6 +59,9 @@ struct EnumString ScriptApiNode::es_ContentParamType2[] = {CPT2_LEVELED, "leveled"}, {CPT2_DEGROTATE, "degrotate"}, {CPT2_MESHOPTIONS, "meshoptions"}, + {CPT2_COLOR, "color"}, + {CPT2_COLORED_FACEDIR, "colorfacedir"}, + {CPT2_COLORED_WALLMOUNTED, "colorwallmounted"}, {0, NULL}, }; diff --git a/src/shader.cpp b/src/shader.cpp index c0ecf738d..79485025b 100644 --- a/src/shader.cpp +++ b/src/shader.cpp @@ -543,7 +543,7 @@ ShaderInfo generate_shader(std::string name, u8 material_type, u8 drawtype, shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL; break; case TILE_MATERIAL_LIQUID_TRANSPARENT: - shaderinfo.base_material = video::EMT_TRANSPARENT_VERTEX_ALPHA; + shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL; break; case TILE_MATERIAL_LIQUID_OPAQUE: shaderinfo.base_material = video::EMT_SOLID; diff --git a/src/wieldmesh.cpp b/src/wieldmesh.cpp index c305238fe..089a67f33 100644 --- a/src/wieldmesh.cpp +++ b/src/wieldmesh.cpp @@ -318,6 +318,7 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client) u32 shader_id = shdrsrc->getShader("wielded_shader", TILE_MATERIAL_BASIC, NDT_NORMAL); m_material_type = shdrsrc->getShaderInfo(shader_id).material; } + m_colors.clear(); // If wield_image is defined, it overrides everything else if (def.wield_image != "") { @@ -358,28 +359,30 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client) material_count = 6; } for (u32 i = 0; i < material_count; ++i) { + const TileSpec *tile = &(f.tiles[i]); video::SMaterial &material = m_meshnode->getMaterial(i); material.setFlag(video::EMF_BACK_FACE_CULLING, true); material.setFlag(video::EMF_BILINEAR_FILTER, m_bilinear_filter); material.setFlag(video::EMF_TRILINEAR_FILTER, m_trilinear_filter); - bool animated = (f.tiles[i].animation_frame_count > 1); + bool animated = (tile->animation_frame_count > 1); if (animated) { - FrameSpec animation_frame = f.tiles[i].frames[0]; + FrameSpec animation_frame = tile->frames[0]; material.setTexture(0, animation_frame.texture); } else { - material.setTexture(0, f.tiles[i].texture); + material.setTexture(0, tile->texture); } + m_colors.push_back(tile->color); material.MaterialType = m_material_type; if (m_enable_shaders) { - if (f.tiles[i].normal_texture) { + if (tile->normal_texture) { if (animated) { - FrameSpec animation_frame = f.tiles[i].frames[0]; + FrameSpec animation_frame = tile->frames[0]; material.setTexture(1, animation_frame.normal_texture); } else { - material.setTexture(1, f.tiles[i].normal_texture); + material.setTexture(1, tile->normal_texture); } } - material.setTexture(2, f.tiles[i].flags_texture); + material.setTexture(2, tile->flags_texture); } } return; @@ -393,11 +396,28 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client) changeToMesh(NULL); } -void WieldMeshSceneNode::setColor(video::SColor color) +void WieldMeshSceneNode::setColor(video::SColor c) { assert(!m_lighting); - setMeshColor(m_meshnode->getMesh(), color); - shadeMeshFaces(m_meshnode->getMesh()); + scene::IMesh *mesh=m_meshnode->getMesh(); + if (mesh == NULL) + return; + + u8 red = c.getRed(); + u8 green = c.getGreen(); + u8 blue = c.getBlue(); + u32 mc = mesh->getMeshBufferCount(); + for (u32 j = 0; j < mc; j++) { + video::SColor bc(0xFFFFFFFF); + if (m_colors.size() > j) + bc = m_colors[j]; + video::SColor buffercolor(255, + bc.getRed() * red / 255, + bc.getGreen() * green / 255, + bc.getBlue() * blue / 255); + scene::IMeshBuffer *buf = mesh->getMeshBuffer(j); + colorizeMeshBuffer(buf, &buffercolor); + } } void WieldMeshSceneNode::render() @@ -464,7 +484,6 @@ scene::IMesh *getItemMesh(Client *client, const ItemStack &item) } else if (f.drawtype == NDT_PLANTLIKE) { mesh = getExtrudedMesh(tsrc, tsrc->getTextureName(f.tiles[0].texture_id)); - return mesh; } else if (f.drawtype == NDT_NORMAL || f.drawtype == NDT_ALLFACES || f.drawtype == NDT_LIQUID || f.drawtype == NDT_FLOWINGLIQUID) { mesh = cloneMesh(g_extrusion_mesh_cache->createCube()); @@ -477,8 +496,6 @@ scene::IMesh *getItemMesh(Client *client, const ItemStack &item) mesh = cloneMesh(mapblock_mesh.getMesh()); translateMesh(mesh, v3f(-BS, -BS, -BS)); scaleMesh(mesh, v3f(0.12, 0.12, 0.12)); - rotateMeshXZby(mesh, -45); - rotateMeshYZby(mesh, -30); u32 mc = mesh->getMeshBufferCount(); for (u32 i = 0; i < mc; ++i) { @@ -492,28 +509,29 @@ scene::IMesh *getItemMesh(Client *client, const ItemStack &item) material1.setTexture(3, material2.getTexture(3)); material1.MaterialType = material2.MaterialType; } - return mesh; } - shadeMeshFaces(mesh); - rotateMeshXZby(mesh, -45); - rotateMeshYZby(mesh, -30); - u32 mc = mesh->getMeshBufferCount(); for (u32 i = 0; i < mc; ++i) { - video::SMaterial &material = mesh->getMeshBuffer(i)->getMaterial(); + const TileSpec *tile = &(f.tiles[i]); + scene::IMeshBuffer *buf = mesh->getMeshBuffer(i); + colorizeMeshBuffer(buf, &tile->color); + video::SMaterial &material = buf->getMaterial(); material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; material.setFlag(video::EMF_BILINEAR_FILTER, false); material.setFlag(video::EMF_TRILINEAR_FILTER, false); material.setFlag(video::EMF_BACK_FACE_CULLING, true); material.setFlag(video::EMF_LIGHTING, false); - if (f.tiles[i].animation_frame_count > 1) { - FrameSpec animation_frame = f.tiles[i].frames[0]; + if (tile->animation_frame_count > 1) { + FrameSpec animation_frame = tile->frames[0]; material.setTexture(0, animation_frame.texture); } else { - material.setTexture(0, f.tiles[i].texture); + material.setTexture(0, tile->texture); } } + + rotateMeshXZby(mesh, -45); + rotateMeshYZby(mesh, -30); return mesh; } return NULL; diff --git a/src/wieldmesh.h b/src/wieldmesh.h index 0162c5e5a..2e78232ae 100644 --- a/src/wieldmesh.h +++ b/src/wieldmesh.h @@ -70,6 +70,11 @@ private: bool m_anisotropic_filter; bool m_bilinear_filter; bool m_trilinear_filter; + /*! + * Stores the colors of the mesh's mesh buffers. + * This does not include lighting. + */ + std::vector m_colors; // Bounding box culling is disabled for this type of scene node, // so this variable is just required so we can implement -- cgit v1.2.3 From 2c19d51409ca903021e0b508e5bc15299c4e51dc Mon Sep 17 00:00:00 2001 From: Loïc Blot Date: Sun, 22 Jan 2017 11:17:41 +0100 Subject: [CSM] sound_play & sound_stop support + client_lua_api doc (#5096) * squashed: CSM: Implement register_globalstep * Re-use fatal error mechanism from server to disconnect client on CSM error * Little client functions cleanups * squashed: CSM: add core.after function * core.after is shared code between client & server * ModApiUtil get_us_time feature enabled for client --- builtin/client/init.lua | 3 +- builtin/client/preview.lua | 15 +- builtin/client/register.lua | 1 + builtin/common/after.lua | 43 +++ builtin/game/init.lua | 1 + builtin/game/misc.lua | 44 --- doc/client_lua_api.txt | 787 ++++++++++++++++++++++++++++++++++++++ src/client.cpp | 1 + src/client.h | 16 +- src/clientenvironment.cpp | 4 + src/clientenvironment.h | 3 + src/guiEngine.h | 1 + src/script/clientscripting.cpp | 2 + src/script/cpp_api/s_client.cpp | 18 + src/script/cpp_api/s_client.h | 2 + src/script/lua_api/CMakeLists.txt | 1 + src/script/lua_api/l_client.h | 3 +- src/script/lua_api/l_mainmenu.cpp | 31 -- src/script/lua_api/l_mainmenu.h | 7 +- src/script/lua_api/l_sound.cpp | 62 +++ src/script/lua_api/l_sound.h | 36 ++ src/script/lua_api/l_util.cpp | 2 + src/script/scripting_mainmenu.cpp | 4 +- 23 files changed, 996 insertions(+), 91 deletions(-) create mode 100644 builtin/common/after.lua create mode 100644 doc/client_lua_api.txt create mode 100644 src/script/lua_api/l_sound.cpp create mode 100644 src/script/lua_api/l_sound.h (limited to 'src/clientenvironment.cpp') diff --git a/builtin/client/init.lua b/builtin/client/init.lua index 4797ac4b6..dd218aab6 100644 --- a/builtin/client/init.lua +++ b/builtin/client/init.lua @@ -4,9 +4,10 @@ local clientpath = scriptpath.."client"..DIR_DELIM local commonpath = scriptpath.."common"..DIR_DELIM dofile(clientpath .. "register.lua") +dofile(commonpath .. "after.lua") +dofile(commonpath .. "chatcommands.lua") dofile(clientpath .. "preview.lua") core.register_on_death(function() core.display_chat_message("You died.") end) - diff --git a/builtin/client/preview.lua b/builtin/client/preview.lua index c421791f5..22e8bb97f 100644 --- a/builtin/client/preview.lua +++ b/builtin/client/preview.lua @@ -1,6 +1,6 @@ -- This is an example function to ensure it's working properly, should be removed before merge core.register_on_shutdown(function() - print("shutdown client") + print("[PREVIEW] shutdown client") end) -- This is an example function to ensure it's working properly, should be removed before merge @@ -15,17 +15,28 @@ core.register_on_sending_chat_messages(function(message) return false end) +-- This is an example function to ensure it's working properly, should be removed before merge core.register_on_hp_modification(function(hp) print("[PREVIEW] HP modified " .. hp) end) +-- This is an example function to ensure it's working properly, should be removed before merge core.register_on_damage_taken(function(hp) print("[PREVIEW] Damage taken " .. hp) end) +-- This is an example function to ensure it's working properly, should be removed before merge +core.register_globalstep(function(dtime) + -- print("[PREVIEW] globalstep " .. dtime) +end) + -- This is an example function to ensure it's working properly, should be removed before merge core.register_chatcommand("dump", { func = function(name, param) return true, dump(_G) end, -}) \ No newline at end of file +}) + +core.after(2, function() + print("After 2") +end) diff --git a/builtin/client/register.lua b/builtin/client/register.lua index ddaf4f424..8b60c1222 100644 --- a/builtin/client/register.lua +++ b/builtin/client/register.lua @@ -55,6 +55,7 @@ local function make_registration() return t, registerfunc end +core.registered_globalsteps, core.register_globalstep = make_registration() core.registered_on_shutdown, core.register_on_shutdown = make_registration() core.registered_on_receiving_chat_messages, core.register_on_receiving_chat_messages = make_registration() core.registered_on_sending_chat_messages, core.register_on_sending_chat_messages = make_registration() diff --git a/builtin/common/after.lua b/builtin/common/after.lua new file mode 100644 index 000000000..30a9c7bad --- /dev/null +++ b/builtin/common/after.lua @@ -0,0 +1,43 @@ +local jobs = {} +local time = 0.0 +local last = core.get_us_time() / 1000000 + +core.register_globalstep(function(dtime) + local new = core.get_us_time() / 1000000 + if new > last then + time = time + (new - last) + else + -- Overflow, we may lose a little bit of time here but + -- only 1 tick max, potentially running timers slightly + -- too early. + time = time + new + end + last = new + + if #jobs < 1 then + return + end + + -- Iterate backwards so that we miss any new timers added by + -- a timer callback, and so that we don't skip the next timer + -- in the list if we remove one. + for i = #jobs, 1, -1 do + local job = jobs[i] + if time >= job.expire then + core.set_last_run_mod(job.mod_origin) + job.func(unpack(job.arg)) + table.remove(jobs, i) + end + end +end) + +function core.after(after, func, ...) + assert(tonumber(after) and type(func) == "function", + "Invalid core.after invocation") + jobs[#jobs + 1] = { + func = func, + expire = time + after, + arg = {...}, + mod_origin = core.get_last_run_mod() + } +end diff --git a/builtin/game/init.lua b/builtin/game/init.lua index 793d9fe2b..3e192a30a 100644 --- a/builtin/game/init.lua +++ b/builtin/game/init.lua @@ -17,6 +17,7 @@ if core.setting_getbool("profiler.load") then profiler = dofile(scriptpath.."profiler"..DIR_DELIM.."init.lua") end +dofile(commonpath .. "after.lua") dofile(gamepath.."item_entity.lua") dofile(gamepath.."deprecated.lua") dofile(gamepath.."misc.lua") diff --git a/builtin/game/misc.lua b/builtin/game/misc.lua index 3419c1980..25376c180 100644 --- a/builtin/game/misc.lua +++ b/builtin/game/misc.lua @@ -4,50 +4,6 @@ -- Misc. API functions -- -local jobs = {} -local time = 0.0 -local last = core.get_us_time() / 1000000 - -core.register_globalstep(function(dtime) - local new = core.get_us_time() / 1000000 - if new > last then - time = time + (new - last) - else - -- Overflow, we may lose a little bit of time here but - -- only 1 tick max, potentially running timers slightly - -- too early. - time = time + new - end - last = new - - if #jobs < 1 then - return - end - - -- Iterate backwards so that we miss any new timers added by - -- a timer callback, and so that we don't skip the next timer - -- in the list if we remove one. - for i = #jobs, 1, -1 do - local job = jobs[i] - if time >= job.expire then - core.set_last_run_mod(job.mod_origin) - job.func(unpack(job.arg)) - table.remove(jobs, i) - end - end -end) - -function core.after(after, func, ...) - assert(tonumber(after) and type(func) == "function", - "Invalid core.after invocation") - jobs[#jobs + 1] = { - func = func, - expire = time + after, - arg = {...}, - mod_origin = core.get_last_run_mod() - } -end - function core.check_player_privs(name, ...) local arg_type = type(name) if (arg_type == "userdata" or arg_type == "table") and diff --git a/doc/client_lua_api.txt b/doc/client_lua_api.txt new file mode 100644 index 000000000..8ce487bf8 --- /dev/null +++ b/doc/client_lua_api.txt @@ -0,0 +1,787 @@ +Minetest Lua Modding API Reference 0.4.15 +========================================= +* More information at +* Developer Wiki: + +Introduction +------------ +Content and functionality can be added to Minetest 0.4 by using Lua +scripting in run-time loaded mods. + +A mod is a self-contained bunch of scripts, textures and other related +things that is loaded by and interfaces with Minetest. + +Mods are contained and ran solely on the server side. Definitions and media +files are automatically transferred to the client. + +If you see a deficiency in the API, feel free to attempt to add the +functionality in the engine and API. You can send such improvements as +source code patches on GitHub (https://github.com/minetest/minetest). + +Programming in Lua +------------------ +If you have any difficulty in understanding this, please read +[Programming in Lua](http://www.lua.org/pil/). + +Startup +------- +Mods are loaded during client startup from the mod load paths by running +the `init.lua` scripts in a shared environment. + +Paths +----- +* `RUN_IN_PLACE=1` (Windows release, local build) + * `$path_user`: + * Linux: `` + * Windows: `` + * `$path_share` + * Linux: `` + * Windows: `` +* `RUN_IN_PLACE=0`: (Linux release) + * `$path_share` + * Linux: `/usr/share/minetest` + * Windows: `/minetest-0.4.x` + * `$path_user`: + * Linux: `$HOME/.minetest` + * Windows: `C:/users//AppData/minetest` (maybe) + +Mod load path +------------- +Generic: + +* `$path_share/clientmods/` +* `$path_user/clientmods/` (User-installed mods) + +In a run-in-place version (e.g. the distributed windows version): + +* `minetest-0.4.x/clientmods/` (User-installed mods) + +On an installed version on Linux: + +* `/usr/share/minetest/clientmods/` +* `$HOME/.minetest/clientmods/` (User-installed mods) + +Modpack support +---------------- +Mods can be put in a subdirectory, if the parent directory, which otherwise +should be a mod, contains a file named `modpack.txt`. This file shall be +empty, except for lines starting with `#`, which are comments. + +Mod directory structure +------------------------ + + mods + |-- modname + | |-- depends.txt + | |-- screenshot.png + | |-- description.txt + | |-- settingtypes.txt + | |-- init.lua + | |-- models + | |-- textures + | | |-- modname_stuff.png + | | `-- modname_something_else.png + | |-- sounds + | |-- media + | `-- + `-- another + + +### modname +The location of this directory can be fetched by using +`minetest.get_modpath(modname)`. + +### `depends.txt` +List of mods that have to be loaded before loading this mod. + +A single line contains a single modname. + +Optional dependencies can be defined by appending a question mark +to a single modname. Their meaning is that if the specified mod +is missing, that does not prevent this mod from being loaded. + +### `screenshot.png` +A screenshot shown in the mod manager within the main menu. It should +have an aspect ratio of 3:2 and a minimum size of 300×200 pixels. + +### `description.txt` +A File containing description to be shown within mainmenu. + +### `settingtypes.txt` +A file in the same format as the one in builtin. It will be parsed by the +settings menu and the settings will be displayed in the "Mods" category. + +### `init.lua` +The main Lua script. Running this script should register everything it +wants to register. Subsequent execution depends on minetest calling the +registered callbacks. + +`minetest.setting_get(name)` and `minetest.setting_getbool(name)` can be used +to read custom or existing settings at load time, if necessary. + +### `sounds` +Media files (sounds) that will be transferred to the +client and will be available for use by the mod. + +Naming convention for registered textual names +---------------------------------------------- +Registered names should generally be in this format: + + "modname:" ( can have characters a-zA-Z0-9_) + +This is to prevent conflicting names from corrupting maps and is +enforced by the mod loader. + +### Example +In the mod `experimental`, there is the ideal item/node/entity name `tnt`. +So the name should be `experimental:tnt`. + +Enforcement can be overridden by prefixing the name with `:`. This can +be used for overriding the registrations of some other mod. + +Example: Any mod can redefine `experimental:tnt` by using the name + + :experimental:tnt + +when registering it. +(also that mod is required to have `experimental` as a dependency) + +The `:` prefix can also be used for maintaining backwards compatibility. + +### Aliases +Aliases can be added by using `minetest.register_alias(name, convert_to)` or +`minetest.register_alias_force(name, convert_to). + +This will make Minetest to convert things called name to things called +`convert_to`. + +The only difference between `minetest.register_alias` and +`minetest.register_alias_force` is that if an item called `name` exists, +`minetest.register_alias` will do nothing while +`minetest.register_alias_force` will unregister it. + +This can be used for maintaining backwards compatibility. + +This can be also used for setting quick access names for things, e.g. if +you have an item called `epiclylongmodname:stuff`, you could do + + minetest.register_alias("stuff", "epiclylongmodname:stuff") + +and be able to use `/giveme stuff`. + +Sounds +------ +Only Ogg Vorbis files are supported. + +For positional playing of sounds, only single-channel (mono) files are +supported. Otherwise OpenAL will play them non-positionally. + +Mods should generally prefix their sounds with `modname_`, e.g. given +the mod name "`foomod`", a sound could be called: + + foomod_foosound.ogg + +Sounds are referred to by their name with a dot, a single digit and the +file extension stripped out. When a sound is played, the actual sound file +is chosen randomly from the matching sounds. + +When playing the sound `foomod_foosound`, the sound is chosen randomly +from the available ones of the following files: + +* `foomod_foosound.ogg` +* `foomod_foosound.0.ogg` +* `foomod_foosound.1.ogg` +* (...) +* `foomod_foosound.9.ogg` + +Examples of sound parameter tables: + + -- Play locationless on all clients + { + gain = 1.0, -- default + } + -- Play locationless to one player + { + to_player = name, + gain = 1.0, -- default + } + -- Play locationless to one player, looped + { + to_player = name, + gain = 1.0, -- default + loop = true, + } + -- Play in a location + { + pos = {x = 1, y = 2, z = 3}, + gain = 1.0, -- default + max_hear_distance = 32, -- default, uses an euclidean metric + } + -- Play connected to an object, looped + { + object = , + gain = 1.0, -- default + max_hear_distance = 32, -- default, uses an euclidean metric + loop = true, + } + +Looped sounds must either be connected to an object or played locationless to +one player using `to_player = name,` + +### `SimpleSoundSpec` +* e.g. `""` +* e.g. `"default_place_node"` +* e.g. `{}` +* e.g. `{name = "default_place_node"}` +* e.g. `{name = "default_place_node", gain = 1.0}` + +Representations of simple things +-------------------------------- + +### Position/vector + + {x=num, y=num, z=num} + +For helper functions see "Vector helpers". + +### `pointed_thing` +* `{type="nothing"}` +* `{type="node", under=pos, above=pos}` +* `{type="object", ref=ObjectRef}` + +Flag Specifier Format +--------------------- +Flags using the standardized flag specifier format can be specified in either of +two ways, by string or table. + +The string format is a comma-delimited set of flag names; whitespace and +unrecognized flag fields are ignored. Specifying a flag in the string sets the +flag, and specifying a flag prefixed by the string `"no"` explicitly +clears the flag from whatever the default may be. + +In addition to the standard string flag format, the schematic flags field can +also be a table of flag names to boolean values representing whether or not the +flag is set. Additionally, if a field with the flag name prefixed with `"no"` +is present, mapped to a boolean of any value, the specified flag is unset. + +E.g. A flag field of value + + {place_center_x = true, place_center_y=false, place_center_z=true} + +is equivalent to + + {place_center_x = true, noplace_center_y=true, place_center_z=true} + +which is equivalent to + + "place_center_x, noplace_center_y, place_center_z" + +or even + + "place_center_x, place_center_z" + +since, by default, no schematic attributes are set. + +Formspec +-------- +Formspec defines a menu. Currently not much else than inventories are +supported. It is a string, with a somewhat strange format. + +Spaces and newlines can be inserted between the blocks, as is used in the +examples. + +### Examples + +#### Chest + + size[8,9] + list[context;main;0,0;8,4;] + list[current_player;main;0,5;8,4;] + +#### Furnace + + size[8,9] + list[context;fuel;2,3;1,1;] + list[context;src;2,1;1,1;] + list[context;dst;5,1;2,2;] + list[current_player;main;0,5;8,4;] + +#### Minecraft-like player inventory + + size[8,7.5] + image[1,0.6;1,2;player.png] + list[current_player;main;0,3.5;8,4;] + list[current_player;craft;3,0;3,3;] + list[current_player;craftpreview;7,1;1,1;] + +### Elements + +#### `size[,,]` +* Define the size of the menu in inventory slots +* `fixed_size`: `true`/`false` (optional) +* deprecated: `invsize[,;]` + +#### `container[,]` +* Start of a container block, moves all physical elements in the container by (X, Y) +* Must have matching container_end +* Containers can be nested, in which case the offsets are added + (child containers are relative to parent containers) + +#### `container_end[]` +* End of a container, following elements are no longer relative to this container + +#### `list[;;,;,;]` +* Show an inventory list + +#### `list[;;,;,;]` +* Show an inventory list + +#### `listring[;]` +* Allows to create a ring of inventory lists +* Shift-clicking on items in one element of the ring + will send them to the next inventory list inside the ring +* The first occurrence of an element inside the ring will + determine the inventory where items will be sent to + +#### `listring[]` +* Shorthand for doing `listring[;]` + for the last two inventory lists added by list[...] + +#### `listcolors[;]` +* Sets background color of slots as `ColorString` +* Sets background color of slots on mouse hovering + +#### `listcolors[;;]` +* Sets background color of slots as `ColorString` +* Sets background color of slots on mouse hovering +* Sets color of slots border + +#### `listcolors[;;;;]` +* Sets background color of slots as `ColorString` +* Sets background color of slots on mouse hovering +* Sets color of slots border +* Sets default background color of tooltips +* Sets default font color of tooltips + +#### `tooltip[;;,]` +* Adds tooltip for an element +* `` tooltip background color as `ColorString` (optional) +* `` tooltip font color as `ColorString` (optional) + +#### `image[,;,;]` +* Show an image +* Position and size units are inventory slots + +#### `item_image[,;,;]` +* Show an inventory image of registered item/node +* Position and size units are inventory slots + +#### `bgcolor[;]` +* Sets background color of formspec as `ColorString` +* If `true`, the background color is drawn fullscreen (does not effect the size of the formspec) + +#### `background[,;,;]` +* Use a background. Inventory rectangles are not drawn then. +* Position and size units are inventory slots +* Example for formspec 8x4 in 16x resolution: image shall be sized + 8 times 16px times 4 times 16px. + +#### `background[,;,;;]` +* Use a background. Inventory rectangles are not drawn then. +* Position and size units are inventory slots +* Example for formspec 8x4 in 16x resolution: + image shall be sized 8 times 16px times 4 times 16px +* If `true` the background is clipped to formspec size + (`x` and `y` are used as offset values, `w` and `h` are ignored) + +#### `pwdfield[,;,;;