voxelands/src/selection_mesh.cpp

367 lines
10 KiB
C++

/************************************************************************
* selection_mesh.cpp
* voxelands - 3d voxel world sandbox game
* Copyright (C) Lisa 'darkrose' Milne 2013-2015 <lisa@ltmnet.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
************************************************************************/
#include "common.h"
#include "common_irrlicht.h"
#include "selection_mesh.h"
#include "content_mapblock.h"
#include "main.h" // For g_settings and g_texturesource
#include "mesh.h"
#include "mapblock.h"
#include "client.h"
static std::map<v3s16,SelectedNode> selected;
static std::vector<SelectionMesh*> meshes;
static v3s16 o_camera_offset(0,0,0);
static void selection_clear()
{
selected.clear();
/* delete meshes */
std::vector<SelectionMesh*> omeshes;
omeshes.swap(meshes);
for (std::vector<SelectionMesh*>::iterator i = omeshes.begin(); i != omeshes.end(); i++) {
SelectionMesh *mesh = *i;
if (mesh)
delete mesh;
}
}
static void selection_generate(Client &client)
{
std::map<v3s16, std::map<v3s16,SelectedNode> > selected_blocks;
Map &map = client.getEnv().getMap();
u32 dn_ratio = client.getEnv().getDayNightRatio();
/* split into mapblocks */
for (std::map<v3s16,SelectedNode>::iterator i = selected.begin(); i != selected.end(); i++) {
v3s16 bp = getNodeBlockPos(i->first);
selected_blocks[bp][i->first] = i->second;
}
/* iterate over mapblocks */
for (std::map<v3s16, std::map<v3s16,SelectedNode> >::iterator i = selected_blocks.begin(); i != selected_blocks.end(); i++) {
/* create selection meshes */
v3s16 bp = i->first;
std::map<v3s16,SelectedNode> bselected = i->second;
MeshMakeData data;
MapBlock *block = map.getBlockNoCreateNoEx(bp);
data.m_selected = bselected;
data.m_env = &client.getEnv();
data.fill(dn_ratio,block);
SelectionMesh *mesh = new SelectionMesh(&data);
meshes.push_back(mesh);
}
}
void selection_draw(video::IVideoDriver* driver, Client &client, v3s16 camera_offset, std::vector<SelectedNode> &select)
{
if (!select.size()) {
if (selected.size())
selection_clear();
return;
}
bool match = true;
std::map<v3s16,SelectedNode> nselected;
for (std::vector<SelectedNode>::iterator i = select.begin(); i != select.end(); i++) {
std::map<v3s16,SelectedNode>::iterator n = selected.find(i->pos);
if (
n == selected.end()
|| n->second.crack != i->crack
|| n->second.has_crack != i->has_crack
|| n->second.is_coloured != i->is_coloured
|| n->second.content != i->content
)
match = false;
nselected[i->pos] = *i;
}
if (!match || nselected.size() != selected.size()) {
selection_clear();
selected.swap(nselected);
o_camera_offset = camera_offset;
selection_generate(client);
}
if (!meshes.size())
return;
v3f cos_diff(0,0,0);
bool cos_changed = false;
if (camera_offset != o_camera_offset) {
cos_diff = intToFloat(o_camera_offset-camera_offset, BS);
o_camera_offset = camera_offset;
cos_changed = true;
}
bool render_trilinear = config_get_bool("client.video.trilinear");
bool render_bilinear = config_get_bool("client.video.bilinear");
bool render_anisotropic = config_get_bool("client.video.anisotropic");
bool anim_textures = config_get_bool("client.graphics.texture.animations");
for (std::vector<SelectionMesh*>::iterator i = meshes.begin(); i != meshes.end(); i++) {
SelectionMesh *mesh = *i;
if (!mesh || !mesh->getMesh())
continue;
scene::SMesh *m = mesh->getMesh();
if (!m)
continue;
if (cos_changed)
translateMesh(m, cos_diff);
if (anim_textures && mesh->isAnimated())
mesh->animate(client.getAnimationTime());
u32 c = m->getMeshBufferCount();
for (u32 i=0; i<c; i++) {
scene::IMeshBuffer *buf = m->getMeshBuffer(i);
if (buf == NULL)
continue;
if (buf->getVertexCount() == 0)
continue;
buf->getMaterial().setFlag(video::EMF_TRILINEAR_FILTER, render_trilinear);
buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, render_bilinear);
buf->getMaterial().setFlag(video::EMF_ANISOTROPIC_FILTER, render_anisotropic);
driver->setMaterial(buf->getMaterial());
driver->drawMeshBuffer(buf);
}
}
}
SelectionMesh::SelectionMesh(MeshMakeData *data):
m_mesh(NULL)
{
generate(data);
}
void SelectionMesh::animate(float time)
{
for (std::map<u32, AnimationData>::iterator it = m_animation_data.begin();
it != m_animation_data.end(); ++it) {
const TileSpec &tile = it->second.tile;
// Figure out current frame
int frame = (int)(time * 1000 / tile.animation_frame_length_ms) % tile.animation_frame_count;
// If frame doesn't change, skip
if (frame == it->second.frame)// || temp_data.frame < 0)
continue;
m_animation_data[it->first].frame = frame;
// Make sure we don't cause an overflow. Can get removed if future is no problems occuring
if (it->first >= m_mesh->getMeshBufferCount())
return;
scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(it->first);
// Create new texture name from original
if (g_texturesource && frame >= 0) {
std::ostringstream os(std::ios::binary);
os << g_texturesource->getTextureName(tile.texture.id);
os << "^[verticalframe:" << (int)tile.animation_frame_count << ":" << frame;
// Set the texture
AtlasPointer ap = g_texturesource->getTexture(os.str());
buf->getMaterial().setTexture(0, ap.atlas);
}
}
}
SelectionMesh::~SelectionMesh()
{
m_mesh->drop();
m_mesh = NULL;
if (!m_animation_data.empty())
m_animation_data.clear();
}
void SelectionMesh::generate(MeshMakeData *data)
{
DSTACK(__FUNCTION_NAME);
data->m_BSd = 0.02;
data->m_BS = (float)BS+data->m_BSd;
data->mesh_detail = config_get_int("client.graphics.mesh.lod");
data->texture_detail = config_get_int("client.graphics.texture.lod");
data->light_detail = config_get_int("client.graphics.light.lod");
m_pos = data->m_blockpos;
for (std::map<v3s16,SelectedNode>::iterator i = data->m_selected.begin(); i != data->m_selected.end(); i++) {
SelectedNode selected = i->second;
v3s16 p = selected.pos-data->m_blockpos_nodes;
MapNode n = data->m_vmanip.getNodeNoEx(selected.pos);
if (data->light_detail > 1 && !selected.is_coloured)
meshgen_preset_smooth_lights(data,p);
switch (content_features(n).draw_type) {
case CDT_AIRLIKE:
break;
case CDT_CUBELIKE:
meshgen_cubelike(data,p,n,selected);
break;
case CDT_DIRTLIKE:
meshgen_dirtlike(data,p,n,selected);
break;
case CDT_RAILLIKE:
meshgen_raillike(data,p,n,selected);
break;
case CDT_PLANTLIKE:
meshgen_plantlike(data,p,n,selected);
break;
case CDT_PLANTLIKE_FERN:
meshgen_plantlike_fern(data,p,n,selected);
break;
case CDT_CROPLIKE:
meshgen_croplike(data,p,n,selected);
break;
case CDT_LIQUID:
meshgen_liquid(data,p,n,selected);
break;
case CDT_LIQUID_SOURCE:
meshgen_liquid_source(data,p,n,selected);
break;
case CDT_NODEBOX:
case CDT_NODEBOX_META:
meshgen_nodebox(data,p,n,selected,false);
break;
case CDT_GLASSLIKE:
meshgen_glasslike(data,p,n,selected);
break;
case CDT_TORCHLIKE:
meshgen_torchlike(data,p,n,selected);
break;
case CDT_FENCELIKE:
meshgen_fencelike(data,p,n,selected);
break;
case CDT_FIRELIKE:
meshgen_firelike(data,p,n,selected);
break;
case CDT_WALLLIKE:
meshgen_walllike(data,p,n,selected);
break;
case CDT_ROOFLIKE:
meshgen_rooflike(data,p,n,selected);
break;
case CDT_LEAFLIKE:
meshgen_leaflike(data,p,n,selected);
break;
case CDT_WIRELIKE:
meshgen_wirelike(data,p,n,selected,false);
break;
case CDT_3DWIRELIKE:
meshgen_wirelike(data,p,n,selected,true);
break;
case CDT_STAIRLIKE:
meshgen_stairlike(data,p,n,selected);
break;
case CDT_SLABLIKE:
meshgen_slablike(data,p,n,selected);
break;
case CDT_TRUNKLIKE:
meshgen_trunklike(data,p,n,selected);
break;
case CDT_FLAGLIKE:
meshgen_flaglike(data,p,n,selected);
break;
case CDT_MELONLIKE:
meshgen_melonlike(data,p,n,selected);
break;
case CDT_CAMPFIRELIKE:
meshgen_campfirelike(data,p,n,selected);
break;
case CDT_BUSHLIKE:
meshgen_bushlike(data,p,n,selected);
break;
default:;
}
}
scene::SMesh *mesh = new scene::SMesh();
for (u32 i=0; i<data->m_meshdata.size(); i++) {
MeshData &d = data->m_meshdata[i];
// - Texture animation
if (d.tile.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES) {
// Add to MapBlockMesh in order to animate these tiles
AnimationData anim_data;
anim_data.tile = d.tile;
anim_data.frame = -1;
m_animation_data[i] = anim_data;
}
// Create meshbuffer
// This is a "Standard MeshBuffer",
// it's a typedeffed CMeshBuffer<video::S3DVertex>
scene::SMeshBuffer *buf = new scene::SMeshBuffer();
// Set material
buf->Material = d.tile.getMaterial();
// Add to mesh
mesh->addMeshBuffer(buf);
// Mesh grabbed it
buf->drop();
buf->append(d.vertices.data(), d.vertices.size(), d.indices.data(), d.indices.size());
}
translateMesh(mesh, intToFloat(data->m_blockpos_nodes - o_camera_offset, BS));
if (m_mesh)
m_mesh->drop();
m_mesh = mesh;
m_meshdata.swap(data->m_meshdata);
refresh(data->m_daynight_ratio);
m_mesh->recalculateBoundingBox();
animate(0.0); // init first frame
}
void SelectionMesh::refresh(u32 daynight_ratio)
{
if (m_mesh == NULL)
return;
u16 mc = m_mesh->getMeshBufferCount();
for (u16 j=0; j<mc; j++) {
scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(j);
if (buf == 0)
continue;
u16 vc = buf->getVertexCount();
if (!vc)
continue;
video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
if (vertices == 0)
continue;
u32 *c = m_meshdata[j].colours.data();
for (u16 i=0; i<vc; i++) {
vertices[i].Color = blend_light(c[i],daynight_ratio);
}
}
}