bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio) { if(!m_has_animation) { m_animation_force_timer = 100000; return false; } m_animation_force_timer = myrand_range(5, 100); // Cracks if(crack != m_last_crack) { for(std::map<u32, std::string>::iterator i = m_crack_materials.begin(); i != m_crack_materials.end(); i++) { scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first); std::string basename = i->second; // Create new texture name from original ITextureSource *tsrc = m_gamedef->getTextureSource(); std::ostringstream os; os<<basename<<crack; AtlasPointer ap = tsrc->getTexture(os.str()); buf->getMaterial().setTexture(0, ap.atlas); } m_last_crack = crack; } // Day-night transition if(daynight_ratio != m_last_daynight_ratio) { for(std::map<u32, std::map<u32, std::pair<u8, u8> > >::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<u32, std::pair<u8, u8 > >::iterator j = i->second.begin(); j != i->second.end(); j++) { u32 vertexIndex = j->first; u8 day = j->second.first; u8 night = j->second.second; finalColorBlend(vertices[vertexIndex].Color, day, night, daynight_ratio); } } m_last_daynight_ratio = daynight_ratio; } return true; }
GUIChatConsole::GUIChatConsole( gui::IGUIEnvironment* env, gui::IGUIElement* parent, s32 id, ChatBackend* backend, Client* client, IMenuManager* menumgr ): IGUIElement(gui::EGUIET_ELEMENT, env, parent, id, core::rect<s32>(0,0,100,100)), m_chat_backend(backend), m_client(client), m_menumgr(menumgr), m_animate_time_old(porting::getTimeMs()) { // load background settings s32 console_alpha = g_settings->getS32("console_alpha"); m_background_color.setAlpha(clamp_u8(console_alpha)); // load the background texture depending on settings ITextureSource *tsrc = client->getTextureSource(); if (tsrc->isKnownSourceImage("background_chat.jpg")) { m_background = tsrc->getTexture("background_chat.jpg"); m_background_color.setRed(255); m_background_color.setGreen(255); m_background_color.setBlue(255); } else { v3f console_color = g_settings->getV3F("console_color"); m_background_color.setRed(clamp_u8(myround(console_color.X))); m_background_color.setGreen(clamp_u8(myround(console_color.Y))); m_background_color.setBlue(clamp_u8(myround(console_color.Z))); } m_font = g_fontengine->getFont(FONT_SIZE_UNSPECIFIED, FM_Mono); if (!m_font) { errorstream << "GUIChatConsole: Unable to load mono font "; } else { core::dimension2d<u32> dim = m_font->getDimension(L"M"); m_fontsize = v2u32(dim.Width, dim.Height); m_font->grab(); } m_fontsize.X = MYMAX(m_fontsize.X, 1); m_fontsize.Y = MYMAX(m_fontsize.Y, 1); // set default cursor options setCursor(true, true, 2.0, 0.1); }
ClientCached* createClientCachedDirect(const std::string &name, Client *client) const { infostream<<"Lazily creating item texture and mesh for \"" <<name<<"\""<<std::endl; // This is not thread-safe sanity_check(std::this_thread::get_id() == m_main_thread); // Skip if already in cache ClientCached *cc = NULL; m_clientcached.get(name, &cc); if(cc) return cc; ITextureSource *tsrc = client->getTextureSource(); const ItemDefinition &def = get(name); // Create new ClientCached cc = new ClientCached(); // Create an inventory texture cc->inventory_texture = NULL; if (!def.inventory_image.empty()) cc->inventory_texture = tsrc->getTexture(def.inventory_image); ItemStack item = ItemStack(); item.name = def.name; getItemMesh(client, item, &(cc->wield_mesh)); cc->palette = tsrc->getPalette(def.palette_image); // Put in cache m_clientcached.set(name, cc); return cc; }
ClientCached* createClientCachedDirect(const std::string &name, IGameDef *gamedef) const { infostream<<"Lazily creating item texture and mesh for \"" <<name<<"\""<<std::endl; // This is not thread-safe sanity_check(thr_is_current_thread(m_main_thread)); // Skip if already in cache ClientCached *cc = NULL; m_clientcached.get(name, &cc); if(cc) return cc; ITextureSource *tsrc = gamedef->getTextureSource(); const ItemDefinition &def = get(name); // Create new ClientCached cc = new ClientCached(); // Create an inventory texture cc->inventory_texture = NULL; if(def.inventory_image != "") cc->inventory_texture = tsrc->getTexture(def.inventory_image); ItemStack item = ItemStack(); item.name = def.name; scene::IMesh *mesh = getItemMesh(gamedef, item); cc->wield_mesh = mesh; // Put in cache m_clientcached.set(name, cc); return cc; }
void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result) { ITextureSource *tsrc = client->getTextureSource(); IItemDefManager *idef = client->getItemDefManager(); const NodeDefManager *ndef = client->getNodeDefManager(); const ItemDefinition &def = item.getDefinition(idef); const ContentFeatures &f = ndef->get(def.name); content_t id = ndef->getId(def.name); FATAL_ERROR_IF(!g_extrusion_mesh_cache, "Extrusion mesh cache is not yet initialized"); scene::SMesh *mesh = nullptr; // Shading is on by default result->needs_shading = true; // If inventory_image is defined, it overrides everything else if (!def.inventory_image.empty()) { mesh = getExtrudedMesh(tsrc, def.inventory_image, def.inventory_overlay); result->buffer_colors.emplace_back(); // overlay is white, if present result->buffer_colors.emplace_back(true, video::SColor(0xFFFFFFFF)); // Items with inventory images do not need shading result->needs_shading = false; } else if (def.type == ITEM_NODE) { if (f.mesh_ptr[0]) { mesh = cloneMesh(f.mesh_ptr[0]); scaleMesh(mesh, v3f(0.12, 0.12, 0.12)); postProcessNodeMesh(mesh, f, false, false, nullptr, &result->buffer_colors); } else { switch (f.drawtype) { case NDT_PLANTLIKE: { mesh = getExtrudedMesh(tsrc, tsrc->getTextureName(f.tiles[0].layers[0].texture_id), tsrc->getTextureName(f.tiles[0].layers[1].texture_id)); // Add color const TileLayer &l0 = f.tiles[0].layers[0]; result->buffer_colors.emplace_back(l0.has_color, l0.color); const TileLayer &l1 = f.tiles[0].layers[1]; result->buffer_colors.emplace_back(l1.has_color, l1.color); break; } case NDT_PLANTLIKE_ROOTED: { mesh = getExtrudedMesh(tsrc, tsrc->getTextureName(f.special_tiles[0].layers[0].texture_id), ""); // Add color const TileLayer &l0 = f.special_tiles[0].layers[0]; result->buffer_colors.emplace_back(l0.has_color, l0.color); break; } case NDT_NORMAL: case NDT_ALLFACES: case NDT_LIQUID: case NDT_FLOWINGLIQUID: { scene::IMesh *cube = g_extrusion_mesh_cache->createCube(); mesh = cloneMesh(cube); cube->drop(); scaleMesh(mesh, v3f(1.2, 1.2, 1.2)); // add overlays postProcessNodeMesh(mesh, f, false, false, nullptr, &result->buffer_colors); break; } default: { mesh = createSpecialNodeMesh(client, id, &result->buffer_colors); scaleMesh(mesh, v3f(0.12, 0.12, 0.12)); } } } u32 mc = mesh->getMeshBufferCount(); for (u32 i = 0; i < mc; ++i) { scene::IMeshBuffer *buf = mesh->getMeshBuffer(i); 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); } rotateMeshXZby(mesh, -45); rotateMeshYZby(mesh, -30); } result->mesh = mesh; }
void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client) { ITextureSource *tsrc = client->getTextureSource(); IItemDefManager *idef = client->getItemDefManager(); IShaderSource *shdrsrc = client->getShaderSource(); const NodeDefManager *ndef = client->getNodeDefManager(); const ItemDefinition &def = item.getDefinition(idef); const ContentFeatures &f = ndef->get(def.name); content_t id = ndef->getId(def.name); scene::SMesh *mesh = nullptr; if (m_enable_shaders) { u32 shader_id = shdrsrc->getShader("wielded_shader", TILE_MATERIAL_BASIC, NDT_NORMAL); m_material_type = shdrsrc->getShaderInfo(shader_id).material; } // Color-related m_colors.clear(); m_base_color = idef->getItemstackColor(item, client); // If wield_image is defined, it overrides everything else if (!def.wield_image.empty()) { setExtruded(def.wield_image, def.wield_overlay, def.wield_scale, tsrc, 1); m_colors.emplace_back(); // overlay is white, if present m_colors.emplace_back(true, video::SColor(0xFFFFFFFF)); return; } // Handle nodes // See also CItemDefManager::createClientCached() if (def.type == ITEM_NODE) { if (f.mesh_ptr[0]) { // e.g. mesh nodes and nodeboxes mesh = cloneMesh(f.mesh_ptr[0]); postProcessNodeMesh(mesh, f, m_enable_shaders, true, &m_material_type, &m_colors); changeToMesh(mesh); mesh->drop(); // mesh is pre-scaled by BS * f->visual_scale m_meshnode->setScale( def.wield_scale * WIELD_SCALE_FACTOR / (BS * f.visual_scale)); } else { switch (f.drawtype) { case NDT_AIRLIKE: { changeToMesh(nullptr); break; } case NDT_PLANTLIKE: { setExtruded(tsrc->getTextureName(f.tiles[0].layers[0].texture_id), tsrc->getTextureName(f.tiles[0].layers[1].texture_id), def.wield_scale, tsrc, f.tiles[0].layers[0].animation_frame_count); // Add color const TileLayer &l0 = f.tiles[0].layers[0]; m_colors.emplace_back(l0.has_color, l0.color); const TileLayer &l1 = f.tiles[0].layers[1]; m_colors.emplace_back(l1.has_color, l1.color); break; } case NDT_PLANTLIKE_ROOTED: { setExtruded(tsrc->getTextureName(f.special_tiles[0].layers[0].texture_id), "", def.wield_scale, tsrc, f.special_tiles[0].layers[0].animation_frame_count); // Add color const TileLayer &l0 = f.special_tiles[0].layers[0]; m_colors.emplace_back(l0.has_color, l0.color); break; } case NDT_NORMAL: case NDT_ALLFACES: case NDT_LIQUID: case NDT_FLOWINGLIQUID: { setCube(f, def.wield_scale); break; } default: { mesh = createSpecialNodeMesh(client, id, &m_colors); changeToMesh(mesh); mesh->drop(); m_meshnode->setScale( def.wield_scale * WIELD_SCALE_FACTOR / (BS * f.visual_scale)); } } } u32 material_count = m_meshnode->getMaterialCount(); for (u32 i = 0; i < material_count; ++i) { video::SMaterial &material = m_meshnode->getMaterial(i); material.MaterialType = m_material_type; 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); } return; } else if (!def.inventory_image.empty()) { setExtruded(def.inventory_image, def.inventory_overlay, def.wield_scale, tsrc, 1); m_colors.emplace_back(); // overlay is white, if present m_colors.emplace_back(true, video::SColor(0xFFFFFFFF)); return; } // no wield mesh found changeToMesh(nullptr); }
bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio) { if(!m_has_animation) { m_animation_force_timer = 100000; return false; } m_animation_force_timer = myrand_range(5, 100); // Cracks if(crack != m_last_crack) { for(std::map<u32, std::string>::iterator i = m_crack_materials.begin(); i != m_crack_materials.end(); i++) { scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first); std::string basename = i->second; // Create new texture name from original ITextureSource *tsrc = m_gamedef->getTextureSource(); std::ostringstream os; os<<basename<<crack; u32 new_texture_id = 0; video::ITexture *new_texture = tsrc->getTexture(os.str(), &new_texture_id); buf->getMaterial().setTexture(0, new_texture); // If the current material is also animated, // update animation info std::map<u32, TileSpec>::iterator anim_iter = m_animation_tiles.find(i->first); if(anim_iter != m_animation_tiles.end()){ TileSpec &tile = anim_iter->second; tile.texture = new_texture; tile.texture_id = new_texture_id; // force animation update m_animation_frames[i->first] = -1; } } m_last_crack = crack; } // Texture animation for(std::map<u32, TileSpec>::iterator i = m_animation_tiles.begin(); i != m_animation_tiles.end(); i++) { const TileSpec &tile = i->second; // Figure out current frame int frameoffset = m_animation_frame_offsets[i->first]; int frame = (int)(time * 1000 / tile.animation_frame_length_ms + frameoffset) % (tile.animation_frame_count ? tile.animation_frame_count : 1); // If frame doesn't change, skip if(frame == m_animation_frames[i->first]) continue; m_animation_frames[i->first] = frame; scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first); ITextureSource *tsrc = m_gamedef->getTextureSource(); FrameSpec animation_frame = tile.frames.find(frame)->second; buf->getMaterial().setTexture(0, animation_frame.texture); if (m_enable_shaders) { if (animation_frame.normal_texture) { buf->getMaterial().setTexture(1, animation_frame.normal_texture); buf->getMaterial().setTexture(2, tsrc->getTexture("enable_img.png")); } else { buf->getMaterial().setTexture(2, tsrc->getTexture("disable_img.png")); } } } // Day-night transition if(!m_enable_shaders && (daynight_ratio != m_last_daynight_ratio)) { for(std::map<u32, std::map<u32, std::pair<u8, u8> > >::iterator i = m_daynight_diffs.begin(); i != m_daynight_diffs.end(); i++) { scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first); buf->setDirty(irr::scene::EBT_VERTEX); video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices(); for(std::map<u32, std::pair<u8, u8 > >::iterator j = i->second.begin(); j != i->second.end(); j++) { u32 vertexIndex = j->first; u8 day = j->second.first; u8 night = j->second.second; finalColorBlend(vertices[vertexIndex].Color, day, night, daynight_ratio); } } m_last_daynight_ratio = daynight_ratio; } // Node highlighting if (m_enable_highlighting) { u8 day = m_highlight_mesh_color.getRed(); u8 night = m_highlight_mesh_color.getGreen(); video::SColor hc; finalColorBlend(hc, day, night, daynight_ratio); float sin_r = 0.07 * sin(1.5 * time); float sin_g = 0.07 * sin(1.5 * time + irr::core::PI * 0.5); float sin_b = 0.07 * sin(1.5 * time + irr::core::PI); hc.setRed(core::clamp(core::round32(hc.getRed() * (0.8 + sin_r)), 0, 255)); hc.setGreen(core::clamp(core::round32(hc.getGreen() * (0.8 + sin_g)), 0, 255)); hc.setBlue(core::clamp(core::round32(hc.getBlue() * (0.8 + sin_b)), 0, 255)); for(std::list<u32>::iterator i = m_highlighted_materials.begin(); i != m_highlighted_materials.end(); i++) { scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(*i); video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices(); for (u32 j = 0; j < buf->getVertexCount() ;j++) vertices[j].Color = hc; } } return true; }
MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset): clearHardwareBuffer(false), step(data->step), timestamp(data->timestamp), m_mesh(new scene::SMesh()), m_gamedef(data->m_gamedef), m_animation_force_timer(0), // force initial animation m_last_crack(-1), m_crack_materials(), m_highlighted_materials(), m_last_daynight_ratio((u32) -1), m_daynight_diffs(), m_usage_timer(0) { m_enable_shaders = g_settings->getBool("enable_shaders"); m_enable_highlighting = g_settings->getBool("enable_node_highlighting"); // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated) // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated) //TimeTaker timer1("MapBlockMesh()"); data->fill_data(); std::vector<FastFace> fastfaces_new; /* We are including the faces of the trailing edges of the block. This means that when something changes, the caller must also update the meshes of the blocks at the leading edges. NOTE: This is the slowest part of this method. */ { // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated) //TimeTaker timer2("updateAllFastFaceRows()"); updateAllFastFaceRows(data, fastfaces_new, step); } // End of slow part //if (data->debug) infostream<<" step="<<step<<" fastfaces_new.size="<<fastfaces_new.size()<<std::endl; /* Convert FastFaces to MeshCollector */ MeshCollector collector; { // avg 0ms (100ms spikes when loading textures the first time) // (NOTE: probably outdated) //TimeTaker timer2("MeshCollector building"); for(u32 i=0; i<fastfaces_new.size(); i++) { FastFace &f = fastfaces_new[i]; const u16 indices[] = {0,1,2,2,3,0}; const u16 indices_alternate[] = {0,1,3,2,3,1}; if(f.tile.texture == NULL) continue; 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. */ if(f.vertices[0].Color.getRed() != f.vertices[2].Color.getRed() || f.vertices[1].Color.getRed() == f.vertices[3].Color.getRed()) indices_p = indices_alternate; collector.append(f.tile, f.vertices, 4, indices_p, 6); } } /* Add special graphics: - torches - flowing water - fences - whatever */ if(step <= 1) mapblock_mesh_generate_special(data, collector); m_highlight_mesh_color = data->m_highlight_mesh_color; /* Convert MeshCollector to SMesh */ ITextureSource *tsrc = m_gamedef->tsrc(); IShaderSource *shdrsrc = m_gamedef->getShaderSource(); for(u32 i = 0; i < collector.prebuffers.size(); i++) { PreMeshBuffer &p = collector.prebuffers[i]; if (step <= data->draw_control.farmesh || !data->draw_control.farmesh) { // Generate animation data // - Cracks if(p.tile.material_flags & MATERIAL_FLAG_CRACK) { // Find the texture name plus ^[crack:N: std::ostringstream os(std::ios::binary); os<<tsrc->getTextureName(p.tile.texture_id)<<"^[crack"; if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY) os<<"o"; // use ^[cracko os<<":"<<(u32)p.tile.animation_frame_count<<":"; m_crack_materials.insert(std::make_pair(i, os.str())); // Replace tile texture with the cracked one p.tile.texture = tsrc->getTexture( os.str()+"0", &p.tile.texture_id); } } // - Texture animation if(p.tile.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES) { // Add to MapBlockMesh in order to animate these tiles m_animation_tiles[i] = p.tile; m_animation_frames[i] = 0; if(g_settings->getBool("desynchronize_mapblock_texture_animation")){ // Get starting position from noise m_animation_frame_offsets[i] = 100000 * (2.0 + noise3d( data->m_blockpos.X, data->m_blockpos.Y, data->m_blockpos.Z, 0)); } else { // Play all synchronized m_animation_frame_offsets[i] = 0; } // Replace tile texture with the first animation frame FrameSpec animation_frame = p.tile.frames.find(0)->second; p.tile.texture = animation_frame.texture; } if(m_enable_highlighting && p.tile.material_flags & MATERIAL_FLAG_HIGHLIGHTED) m_highlighted_materials.push_back(i); for(u32 j = 0; j < p.vertices.size(); j++) { // Note applyFacesShading second parameter is precalculated sqrt // value for speed improvement // Skip it for lightsources and top faces. video::SColor &vc = p.vertices[j].Color; if (!vc.getBlue()) { if (p.vertices[j].Normal.Y < -0.5) { applyFacesShading (vc, 0.447213); } else if (p.vertices[j].Normal.X > 0.5) { applyFacesShading (vc, 0.670820); } else if (p.vertices[j].Normal.X < -0.5) { applyFacesShading (vc, 0.670820); } else if (p.vertices[j].Normal.Z > 0.5) { applyFacesShading (vc, 0.836660); } else if (p.vertices[j].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); } } // Create material video::SMaterial material; material.setFlag(video::EMF_LIGHTING, false); material.setFlag(video::EMF_BACK_FACE_CULLING, true); material.setFlag(video::EMF_BILINEAR_FILTER, false); material.setFlag(video::EMF_FOG_ENABLE, true); //material.setFlag(video::EMF_WIREFRAME, true); material.setTexture(0, p.tile.texture); if (p.tile.material_flags & MATERIAL_FLAG_HIGHLIGHTED) { material.MaterialType = video::EMT_TRANSPARENT_ADD_COLOR; } else { if (m_enable_shaders) { material.MaterialType = shdrsrc->getShaderInfo(p.tile.shader_id).material; p.tile.applyMaterialOptionsWithShaders(material); if (p.tile.normal_texture) { material.setTexture(1, p.tile.normal_texture); material.setTexture(2, tsrc->getTexture("enable_img.png")); } else { material.setTexture(2, tsrc->getTexture("disable_img.png")); } } else { p.tile.applyMaterialOptions(material); } } // Create meshbuffer // This is a "Standard MeshBuffer", // it's a typedeffed CMeshBuffer<video::S3DVertex> scene::SMeshBuffer *buf = new scene::SMeshBuffer(); // Set material buf->Material = material; // Add to mesh m_mesh->addMeshBuffer(buf); // Mesh grabbed it buf->drop(); buf->append(&p.vertices[0], p.vertices.size(), &p.indices[0], p.indices.size()); } m_camera_offset = camera_offset; /* Do some stuff to the mesh */ v3f t = v3f(0,0,0); if (step>1) { scaleMesh(m_mesh, v3f(step,step,step)); // TODO: remove this wrong numbers, find formula good test: fly above ocean if (step == 2) t = v3f(BS/2, BS/2, BS/2); if (step == 4) t = v3f(BS*1.666, -BS/3.0, BS*1.666); if (step == 8) t = v3f(BS*2.666, -BS*2.4, BS*2.666); if (step == 16) t = v3f(BS*6.4, -BS*6.4, BS*6.4); } translateMesh(m_mesh, intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS) + t); if(m_mesh) { #if 0 // Usually 1-700 faces and 1-7 materials infostream<<"Updated MapBlock mesh p="<<data->m_blockpos<<" has "<<fastfaces_new.size()<<" faces " <<"and uses "<<m_mesh->getMeshBufferCount() <<" materials "<<" step="<<step<<" range="<<data->range<< " mesh="<<m_mesh<<std::endl; #endif } //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl; // Check if animation is required for this mesh m_has_animation = !m_crack_materials.empty() || !m_daynight_diffs.empty() || !m_animation_tiles.empty() || !m_highlighted_materials.empty(); }
MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset): m_mesh(new scene::SMesh()), m_gamedef(data->m_gamedef), m_animation_force_timer(0), // force initial animation m_last_crack(-1), m_crack_materials(), m_last_daynight_ratio((u32) -1), m_daynight_diffs() { // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated) // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated) //TimeTaker timer1("MapBlockMesh()"); std::vector<FastFace> fastfaces_new; /* We are including the faces of the trailing edges of the block. This means that when something changes, the caller must also update the meshes of the blocks at the leading edges. NOTE: This is the slowest part of this method. */ { // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated) //TimeTaker timer2("updateAllFastFaceRows()"); updateAllFastFaceRows(data, fastfaces_new); } // End of slow part /* Convert FastFaces to MeshCollector */ MeshCollector collector; { // avg 0ms (100ms spikes when loading textures the first time) // (NOTE: probably outdated) //TimeTaker timer2("MeshCollector building"); for(u32 i=0; i<fastfaces_new.size(); i++) { FastFace &f = fastfaces_new[i]; const u16 indices[] = {0,1,2,2,3,0}; const u16 indices_alternate[] = {0,1,3,2,3,1}; if(f.tile.texture == NULL) continue; 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. */ if(f.vertices[0].Color.getRed() != f.vertices[2].Color.getRed() || f.vertices[1].Color.getRed() == f.vertices[3].Color.getRed()) indices_p = indices_alternate; collector.append(f.tile, f.vertices, 4, indices_p, 6); } } /* Add special graphics: - torches - flowing water - fences - whatever */ mapblock_mesh_generate_special(data, collector); /* Convert MeshCollector to SMesh */ bool enable_shaders = g_settings->getBool("enable_shaders"); bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping"); bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion"); video::E_MATERIAL_TYPE shadermat1, shadermat2, shadermat3, shadermat4, shadermat5; shadermat1 = shadermat2 = shadermat3 = shadermat4 = shadermat5 = video::EMT_SOLID; if (enable_shaders) { IShaderSource *shdrsrc = m_gamedef->getShaderSource(); shadermat1 = shdrsrc->getShader("solids_shader").material; shadermat2 = shdrsrc->getShader("liquids_shader").material; shadermat3 = shdrsrc->getShader("alpha_shader").material; shadermat4 = shdrsrc->getShader("leaves_shader").material; shadermat5 = shdrsrc->getShader("plants_shader").material; } for(u32 i = 0; i < collector.prebuffers.size(); i++) { PreMeshBuffer &p = collector.prebuffers[i]; /*dstream<<"p.vertices.size()="<<p.vertices.size() <<", p.indices.size()="<<p.indices.size() <<std::endl;*/ // Generate animation data // - Cracks if(p.tile.material_flags & MATERIAL_FLAG_CRACK) { ITextureSource *tsrc = data->m_gamedef->tsrc(); // Find the texture name plus ^[crack:N: std::ostringstream os(std::ios::binary); os<<tsrc->getTextureName(p.tile.texture_id)<<"^[crack"; if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY) os<<"o"; // use ^[cracko os<<":"<<(u32)p.tile.animation_frame_count<<":"; m_crack_materials.insert(std::make_pair(i, os.str())); // Replace tile texture with the cracked one p.tile.texture = tsrc->getTexture( os.str()+"0", &p.tile.texture_id); } // - Texture animation if(p.tile.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES) { ITextureSource *tsrc = data->m_gamedef->tsrc(); // Add to MapBlockMesh in order to animate these tiles m_animation_tiles[i] = p.tile; m_animation_frames[i] = 0; if(g_settings->getBool("desynchronize_mapblock_texture_animation")){ // Get starting position from noise m_animation_frame_offsets[i] = 100000 * (2.0 + noise3d( data->m_blockpos.X, data->m_blockpos.Y, data->m_blockpos.Z, 0)); } else { // Play all synchronized m_animation_frame_offsets[i] = 0; } // Replace tile texture with the first animation frame std::ostringstream os(std::ios::binary); os<<tsrc->getTextureName(p.tile.texture_id); os<<"^[verticalframe:"<<(int)p.tile.animation_frame_count<<":0"; p.tile.texture = tsrc->getTexture( os.str(), &p.tile.texture_id); } // - Classic lighting (shaders handle this by themselves) if(!enable_shaders) { for(u32 j = 0; j < p.vertices.size(); j++) { video::SColor &vc = p.vertices[j].Color; // 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); // Brighten topside (no shaders) if(p.vertices[j].Normal.Y > 0.5) { vc.setRed (srgb_linear_multiply(vc.getRed(), 1.3, 255.0)); vc.setGreen(srgb_linear_multiply(vc.getGreen(), 1.3, 255.0)); vc.setBlue (srgb_linear_multiply(vc.getBlue(), 1.3, 255.0)); } } } // Create material video::SMaterial material; material.setFlag(video::EMF_LIGHTING, false); material.setFlag(video::EMF_BACK_FACE_CULLING, true); material.setFlag(video::EMF_BILINEAR_FILTER, false); material.setFlag(video::EMF_FOG_ENABLE, true); //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF); //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE); //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; material.setTexture(0, p.tile.texture); if (enable_shaders) { ITextureSource *tsrc = data->m_gamedef->tsrc(); material.setTexture(2, tsrc->getTexture("disable_img.png")); if (enable_bumpmapping || enable_parallax_occlusion) { std::string fname_base = tsrc->getTextureName(p.tile.texture_id); std::string normal_ext = "_normal.png"; size_t pos = fname_base.find("."); std::string fname_normal = fname_base.substr(0, pos) + normal_ext; if (tsrc->isKnownSourceImage(fname_normal)) { // look for image extension and replace it size_t i = 0; while ((i = fname_base.find(".", i)) != std::string::npos) { fname_base.replace(i, 4, normal_ext); i += normal_ext.length(); } material.setTexture(1, tsrc->getTexture(fname_base)); material.setTexture(2, tsrc->getTexture("enable_img.png")); } } p.tile.applyMaterialOptionsWithShaders(material, shadermat1, shadermat2, shadermat3, shadermat4, shadermat5); } else { p.tile.applyMaterialOptions(material); } // Create meshbuffer // This is a "Standard MeshBuffer", // it's a typedeffed CMeshBuffer<video::S3DVertex> scene::SMeshBuffer *buf = new scene::SMeshBuffer(); // Set material buf->Material = material; // Add to mesh m_mesh->addMeshBuffer(buf); // Mesh grabbed it buf->drop(); buf->append(&p.vertices[0], p.vertices.size(), &p.indices[0], p.indices.size()); } m_camera_offset = camera_offset; /* Do some stuff to the mesh */ translateMesh(m_mesh, intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS)); if(m_mesh) { #if 0 // Usually 1-700 faces and 1-7 materials std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces " <<"and uses "<<m_mesh->getMeshBufferCount() <<" materials (meshbuffers)"<<std::endl; #endif // Use VBO for mesh (this just would set this for ever buffer) // This will lead to infinite memory usage because or irrlicht. //m_mesh->setHardwareMappingHint(scene::EHM_STATIC); /* NOTE: If that is enabled, some kind of a queue to the main thread should be made which would call irrlicht to delete the hardware buffer and then delete the mesh */ } //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl; // Check if animation is required for this mesh m_has_animation = !m_crack_materials.empty() || !m_daynight_diffs.empty() || !m_animation_tiles.empty(); }
bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio) { if(!m_has_animation) { m_animation_force_timer = 100000; return false; } m_animation_force_timer = myrand_range(5, 100); // Cracks if(crack != m_last_crack) { for(std::map<u32, std::string>::iterator i = m_crack_materials.begin(); i != m_crack_materials.end(); i++) { scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first); std::string basename = i->second; // Create new texture name from original ITextureSource *tsrc = m_gamedef->getTextureSource(); std::ostringstream os; os<<basename<<crack; AtlasPointer ap = tsrc->getTexture(os.str()); buf->getMaterial().setTexture(0, ap.atlas); } m_last_crack = crack; } // Texture animation for(std::map<u32, TileSpec>::iterator i = m_animation_tiles.begin(); i != m_animation_tiles.end(); i++) { const TileSpec &tile = i->second; // Figure out current frame int frameoffset = m_animation_frame_offsets[i->first]; int frame = (int)(time * 1000 / tile.animation_frame_length_ms + frameoffset) % tile.animation_frame_count; // If frame doesn't change, skip if(frame == m_animation_frames[i->first]) continue; m_animation_frames[i->first] = frame; scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first); ITextureSource *tsrc = m_gamedef->getTextureSource(); // Create new texture name from original std::ostringstream os(std::ios::binary); os<<tsrc->getTextureName(tile.texture.id); os<<"^[verticalframe:"<<(int)tile.animation_frame_count<<":"<<frame; // Set the texture AtlasPointer ap = tsrc->getTexture(os.str()); buf->getMaterial().setTexture(0, ap.atlas); } // Day-night transition if(daynight_ratio != m_last_daynight_ratio) { for(std::map<u32, std::map<u32, std::pair<u8, u8> > >::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<u32, std::pair<u8, u8 > >::iterator j = i->second.begin(); j != i->second.end(); j++) { u32 vertexIndex = j->first; u8 day = j->second.first; u8 night = j->second.second; finalColorBlend(vertices[vertexIndex].Color, day, night, daynight_ratio); // Brighten topside (no shaders) if(vertices[vertexIndex].Normal.Y > 0.5) { video::SColor &vc = vertices[vertexIndex].Color; vc.setRed (srgb_linear_multiply(vc.getRed(), 1.3, 255.0)); vc.setGreen(srgb_linear_multiply(vc.getGreen(), 1.3, 255.0)); vc.setBlue (srgb_linear_multiply(vc.getBlue(), 1.3, 255.0)); } } } m_last_daynight_ratio = daynight_ratio; } return true; }
ClientCached* createClientCachedDirect(const std::string &name, IGameDef *gamedef) const { infostream<<"Lazily creating item texture and mesh for \"" <<name<<"\""<<std::endl; // This is not thread-safe sanity_check(get_current_thread_id() == m_main_thread); // Skip if already in cache ClientCached *cc = NULL; m_clientcached.get(name, &cc); if(cc) return cc; ITextureSource *tsrc = gamedef->getTextureSource(); INodeDefManager *nodedef = gamedef->getNodeDefManager(); const ItemDefinition &def = get(name); // Create new ClientCached cc = new ClientCached(); // Create an inventory texture cc->inventory_texture = NULL; if(def.inventory_image != "") cc->inventory_texture = tsrc->getTexture(def.inventory_image); // Additional processing for nodes: // - Create a wield mesh if WieldMeshSceneNode can't render // the node on its own. // - If inventory_texture isn't set yet, create one using // render-to-texture. if (def.type == ITEM_NODE) { // Get node properties content_t id = nodedef->getId(name); const ContentFeatures &f = nodedef->get(id); bool need_rtt_mesh = cc->inventory_texture == NULL; // Keep this in sync with WieldMeshSceneNode::setItem() bool need_wield_mesh = !(f.mesh_ptr[0] || f.drawtype == NDT_NORMAL || f.drawtype == NDT_ALLFACES || f.drawtype == NDT_AIRLIKE); scene::IMesh *node_mesh = NULL; if (need_rtt_mesh || need_wield_mesh) { u8 param1 = 0; if (f.param_type == CPT_LIGHT) param1 = 0xee; /* Make a mesh from the node */ MeshMakeData mesh_make_data(gamedef, false); u8 param2 = 0; if (f.param_type_2 == CPT2_WALLMOUNTED) param2 = 1; MapNode mesh_make_node(id, param1, param2); mesh_make_data.fillSingleNode(&mesh_make_node); MapBlockMesh mapblock_mesh(&mesh_make_data, v3s16(0, 0, 0)); node_mesh = mapblock_mesh.getMesh(); node_mesh->grab(); video::SColor c(255, 255, 255, 255); setMeshColor(node_mesh, c); // scale and translate the mesh so it's a // unit cube centered on the origin scaleMesh(node_mesh, v3f(1.0/BS, 1.0/BS, 1.0/BS)); translateMesh(node_mesh, v3f(-1.0, -1.0, -1.0)); } /* Draw node mesh into a render target texture */ if (need_rtt_mesh) { TextureFromMeshParams params; params.mesh = node_mesh; params.dim.set(64, 64); params.rtt_texture_name = "INVENTORY_" + def.name + "_RTT"; params.delete_texture_on_shutdown = true; params.camera_position.set(0, 1.0, -1.5); params.camera_position.rotateXZBy(45); params.camera_lookat.set(0, 0, 0); // Set orthogonal projection params.camera_projection_matrix.buildProjectionMatrixOrthoLH( 1.65, 1.65, 0, 100); params.ambient_light.set(1.0, 0.2, 0.2, 0.2); params.light_position.set(10, 100, -50); params.light_color.set(1.0, 0.5, 0.5, 0.5); params.light_radius = 1000; #ifdef __ANDROID__ params.camera_position.set(0, -1.0, -1.5); params.camera_position.rotateXZBy(45); params.light_position.set(10, -100, -50); #endif cc->inventory_texture = tsrc->generateTextureFromMesh(params); // render-to-target didn't work if (cc->inventory_texture == NULL) { cc->inventory_texture = tsrc->getTexture(f.tiledef[0].name); } } /* Use the node mesh as the wield mesh */ if (need_wield_mesh) { cc->wield_mesh = node_mesh; cc->wield_mesh->grab(); // no way reference count can be smaller than 2 in this place! assert(cc->wield_mesh->getReferenceCount() >= 2); } if (node_mesh) node_mesh->drop(); } // Put in cache m_clientcached.set(name, cc); return cc; }
virtual void updateTexturesAndMeshes(IGameDef *gamedef) { #ifndef SERVER infostream<<"ItemDefManager::updateTexturesAndMeshes(): Updating " <<"textures and meshes in item definitions"<<std::endl; ITextureSource *tsrc = gamedef->getTextureSource(); INodeDefManager *nodedef = gamedef->getNodeDefManager(); IrrlichtDevice *device = tsrc->getDevice(); video::IVideoDriver *driver = device->getVideoDriver(); for(std::map<std::string, ItemDefinition*>::iterator i = m_item_definitions.begin(); i != m_item_definitions.end(); i++) { ItemDefinition *def = i->second; bool need_node_mesh = false; // Create an inventory texture def->inventory_texture = NULL; if(def->inventory_image != "") { def->inventory_texture = tsrc->getTextureRaw(def->inventory_image); } else if(def->type == ITEM_NODE) { need_node_mesh = true; } // Create a wield mesh if(def->wield_mesh != NULL) { def->wield_mesh->drop(); def->wield_mesh = NULL; } if(def->type == ITEM_NODE && def->wield_image == "") { need_node_mesh = true; } else if(def->wield_image != "" || def->inventory_image != "") { // Extrude the wield image into a mesh std::string imagename; if(def->wield_image != "") imagename = def->wield_image; else imagename = def->inventory_image; def->wield_mesh = createExtrudedMesh( tsrc->getTextureRaw(imagename), driver, def->wield_scale * v3f(40.0, 40.0, 4.0)); if(def->wield_mesh == NULL) { infostream<<"ItemDefManager: WARNING: " <<"updateTexturesAndMeshes(): " <<"Unable to create extruded mesh for item " <<def->name<<std::endl; } } if(need_node_mesh) { /* Get node properties */ content_t id = nodedef->getId(def->name); const ContentFeatures &f = nodedef->get(id); u8 param1 = 0; if(f.param_type == CPT_LIGHT) param1 = 0xee; /* Make a mesh from the node */ MeshMakeData mesh_make_data(gamedef); MapNode mesh_make_node(id, param1, 0); mesh_make_data.fillSingleNode(&mesh_make_node); MapBlockMesh mapblock_mesh(&mesh_make_data); scene::IMesh *node_mesh = mapblock_mesh.getMesh(); assert(node_mesh); setMeshColor(node_mesh, video::SColor(255, 255, 255, 255)); /* Scale and translate the mesh so it's a unit cube centered on the origin */ scaleMesh(node_mesh, v3f(1.0/BS, 1.0/BS, 1.0/BS)); translateMesh(node_mesh, v3f(-1.0, -1.0, -1.0)); /* Draw node mesh into a render target texture */ if(def->inventory_texture == NULL) { core::dimension2d<u32> dim(64,64); std::string rtt_texture_name = "INVENTORY_" + def->name + "_RTT"; v3f camera_position(0, 1.0, -1.5); camera_position.rotateXZBy(45); v3f camera_lookat(0, 0, 0); core::CMatrix4<f32> camera_projection_matrix; // Set orthogonal projection camera_projection_matrix.buildProjectionMatrixOrthoLH( 1.65, 1.65, 0, 100); video::SColorf ambient_light(0.2,0.2,0.2); v3f light_position(10, 100, -50); video::SColorf light_color(0.5,0.5,0.5); f32 light_radius = 1000; def->inventory_texture = generateTextureFromMesh( node_mesh, device, dim, rtt_texture_name, camera_position, camera_lookat, camera_projection_matrix, ambient_light, light_position, light_color, light_radius); // render-to-target didn't work if(def->inventory_texture == NULL) { def->inventory_texture = tsrc->getTextureRaw(f.tname_tiles[0]); } } /* Use the node mesh as the wield mesh */ if(def->wield_mesh == NULL) { // Scale to proper wield mesh proportions scaleMesh(node_mesh, v3f(30.0, 30.0, 30.0) * def->wield_scale); def->wield_mesh = node_mesh; def->wield_mesh->grab(); } // falling outside of here deletes node_mesh } } #endif }
void WieldMeshSceneNode::setItem(const ItemStack &item, IGameDef *gamedef) { ITextureSource *tsrc = gamedef->getTextureSource(); IItemDefManager *idef = gamedef->getItemDefManager(); //IShaderSource *shdrsrc = gamedef->getShaderSource(); INodeDefManager *ndef = gamedef->getNodeDefManager(); const ItemDefinition &def = item.getDefinition(idef); const ContentFeatures &f = ndef->get(def.name); content_t id = ndef->getId(def.name); #if 0 //// TODO(RealBadAngel): Reactivate when shader is added for wield items if (m_enable_shaders) { u32 shader_id = shdrsrc->getShader("nodes_shader", TILE_MATERIAL_BASIC, NDT_NORMAL); m_material_type = shdrsrc->getShaderInfo(shader_id).material; } #endif // If wield_image is defined, it overrides everything else if (def.wield_image != "") { setExtruded(def.wield_image, def.wield_scale, tsrc, 1); return; } // Handle nodes // See also CItemDefManager::createClientCached() else if (def.type == ITEM_NODE) { if (f.mesh_ptr[0]) { // e.g. mesh nodes and nodeboxes changeToMesh(f.mesh_ptr[0]); // mesh_ptr[0] is pre-scaled by BS * f->visual_scale m_meshnode->setScale( def.wield_scale * WIELD_SCALE_FACTOR / (BS * f.visual_scale)); } else if (f.drawtype == NDT_AIRLIKE) { changeToMesh(NULL); } else if (f.drawtype == NDT_PLANTLIKE) { setExtruded(tsrc->getTextureName(f.tiles[0].texture_id), def.wield_scale, tsrc, f.tiles[0].animation_frame_count); } else if (f.drawtype == NDT_NORMAL || f.drawtype == NDT_ALLFACES) { setCube(f.tiles, def.wield_scale, tsrc); } else { //// TODO: Change false in the following constructor args to //// appropriate value when shader is added for wield items (if applicable) MeshMakeData mesh_make_data(gamedef, 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)); changeToMesh(mapblock_mesh.getMesh()); translateMesh(m_meshnode->getMesh(), v3f(-BS, -BS, -BS)); m_meshnode->setScale( def.wield_scale * WIELD_SCALE_FACTOR / (BS * f.visual_scale)); } u32 material_count = m_meshnode->getMaterialCount(); if (material_count > 6) { errorstream << "WieldMeshSceneNode::setItem: Invalid material " "count " << material_count << ", truncating to 6" << std::endl; material_count = 6; } for (u32 i = 0; i < material_count; ++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); if (animated) { FrameSpec animation_frame = f.tiles[i].frames[0]; material.setTexture(0, animation_frame.texture); } else { material.setTexture(0, f.tiles[i].texture); } material.MaterialType = m_material_type; #if 0 //// TODO(RealBadAngel): Reactivate when shader is added for wield items if (m_enable_shaders) { if (f.tiles[i].normal_texture) { if (animated) { FrameSpec animation_frame = f.tiles[i].frames[0]; material.setTexture(1, animation_frame.normal_texture); } else { material.setTexture(1, f.tiles[i].normal_texture); } material.setTexture(2, tsrc->getTexture("enable_img.png")); } else { material.setTexture(2, tsrc->getTexture("disable_img.png")); } } #endif } return; } else if (def.inventory_image != "") { setExtruded(def.inventory_image, def.wield_scale, tsrc, 1); return; } // no wield mesh found changeToMesh(NULL); }
MapBlockMesh::MapBlockMesh(MeshMakeData *data): m_mesh(new scene::SMesh()), m_gamedef(data->m_gamedef), m_animation_force_timer(0), // force initial animation m_last_crack(-1), m_crack_materials(), m_last_daynight_ratio((u32) -1), m_daynight_diffs() { // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated) // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated) //TimeTaker timer1("MapBlockMesh()"); core::array<FastFace> fastfaces_new; /* We are including the faces of the trailing edges of the block. This means that when something changes, the caller must also update the meshes of the blocks at the leading edges. NOTE: This is the slowest part of this method. */ { // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated) //TimeTaker timer2("updateAllFastFaceRows()"); updateAllFastFaceRows(data, fastfaces_new); } // End of slow part /* Convert FastFaces to MeshCollector */ MeshCollector collector; { // avg 0ms (100ms spikes when loading textures the first time) // (NOTE: probably outdated) //TimeTaker timer2("MeshCollector building"); for(u32 i=0; i<fastfaces_new.size(); i++) { FastFace &f = fastfaces_new[i]; const u16 indices[] = {0,1,2,2,3,0}; const u16 indices_alternate[] = {0,1,3,2,3,1}; if(f.tile.texture.atlas == NULL) continue; 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. */ if(f.vertices[0].Color.getRed() != f.vertices[2].Color.getRed() || f.vertices[1].Color.getRed() == f.vertices[3].Color.getRed()) indices_p = indices_alternate; collector.append(f.tile, f.vertices, 4, indices_p, 6); } } /* Add special graphics: - torches - flowing water - fences - whatever */ mapblock_mesh_generate_special(data, collector); /* Convert MeshCollector to SMesh Also store animation info */ for(u32 i = 0; i < collector.prebuffers.size(); i++) { PreMeshBuffer &p = collector.prebuffers[i]; /*dstream<<"p.vertices.size()="<<p.vertices.size() <<", p.indices.size()="<<p.indices.size() <<std::endl;*/ // Generate animation data // - Cracks if(p.tile.material_flags & MATERIAL_FLAG_CRACK) { ITextureSource *tsrc = data->m_gamedef->tsrc(); std::string crack_basename = tsrc->getTextureName(p.tile.texture.id); if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY) crack_basename += "^[cracko"; else crack_basename += "^[crack"; m_crack_materials.insert(std::make_pair(i, crack_basename)); } // - Lighting for(u32 j = 0; j < p.vertices.size(); j++) { video::SColor &vc = p.vertices[j].Color; 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); } // Create material video::SMaterial material; material.setFlag(video::EMF_LIGHTING, false); material.setFlag(video::EMF_BACK_FACE_CULLING, true); material.setFlag(video::EMF_BILINEAR_FILTER, false); material.setFlag(video::EMF_FOG_ENABLE, true); //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF); //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE); material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; material.setTexture(0, p.tile.texture.atlas); p.tile.applyMaterialOptions(material); // Create meshbuffer // This is a "Standard MeshBuffer", // it's a typedeffed CMeshBuffer<video::S3DVertex> scene::SMeshBuffer *buf = new scene::SMeshBuffer(); // Set material buf->Material = material; // Add to mesh m_mesh->addMeshBuffer(buf); // Mesh grabbed it buf->drop(); buf->append(p.vertices.pointer(), p.vertices.size(), p.indices.pointer(), p.indices.size()); } /* Do some stuff to the mesh */ translateMesh(m_mesh, intToFloat(data->m_blockpos * MAP_BLOCKSIZE, BS)); m_mesh->recalculateBoundingBox(); // translateMesh already does this if(m_mesh) { #if 0 // Usually 1-700 faces and 1-7 materials std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces " <<"and uses "<<m_mesh->getMeshBufferCount() <<" materials (meshbuffers)"<<std::endl; #endif // Use VBO for mesh (this just would set this for ever buffer) // This will lead to infinite memory usage because or irrlicht. //m_mesh->setHardwareMappingHint(scene::EHM_STATIC); /* NOTE: If that is enabled, some kind of a queue to the main thread should be made which would call irrlicht to delete the hardware buffer and then delete the mesh */ } //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl; // Check if animation is required for this mesh m_has_animation = !m_crack_materials.empty() || !m_daynight_diffs.empty(); }
ClientCached* createClientCachedDirect(const std::string &name, IGameDef *gamedef) const { infostream<<"Lazily creating item texture and mesh for \"" <<name<<"\""<<std::endl; // This is not thread-safe assert(get_current_thread_id() == m_main_thread); // Skip if already in cache ClientCached *cc = NULL; m_clientcached.get(name, &cc); if(cc) return cc; ITextureSource *tsrc = gamedef->getTextureSource(); INodeDefManager *nodedef = gamedef->getNodeDefManager(); IrrlichtDevice *device = tsrc->getDevice(); video::IVideoDriver *driver = device->getVideoDriver(); const ItemDefinition *def = &get(name); // Create new ClientCached cc = new ClientCached(); bool need_node_mesh = false; // Create an inventory texture cc->inventory_texture = NULL; if(def->inventory_image != "") { cc->inventory_texture = tsrc->getTextureRaw(def->inventory_image); } else if(def->type == ITEM_NODE) { need_node_mesh = true; } // Create a wield mesh assert(cc->wield_mesh == NULL); if(def->type == ITEM_NODE && def->wield_image == "") { need_node_mesh = true; } else if(def->wield_image != "" || def->inventory_image != "") { // Extrude the wield image into a mesh std::string imagename; if(def->wield_image != "") imagename = def->wield_image; else imagename = def->inventory_image; cc->wield_mesh = createExtrudedMesh( tsrc->getTextureRaw(imagename), driver, def->wield_scale * v3f(40.0, 40.0, 4.0)); if(cc->wield_mesh == NULL) { infostream<<"ItemDefManager: WARNING: " <<"updateTexturesAndMeshes(): " <<"Unable to create extruded mesh for item " <<def->name<<std::endl; } } if(need_node_mesh) { /* Get node properties */ content_t id = nodedef->getId(def->name); const ContentFeatures &f = nodedef->get(id); u8 param1 = 0; if(f.param_type == CPT_LIGHT) param1 = 0xee; /* Make a mesh from the node */ MeshMakeData mesh_make_data(gamedef); MapNode mesh_make_node(id, param1, 0); mesh_make_data.fillSingleNode(&mesh_make_node); MapBlockMesh mapblock_mesh(&mesh_make_data); scene::IMesh *node_mesh = mapblock_mesh.getMesh(); assert(node_mesh); video::SColor c(255, 255, 255, 255); if(g_settings->getS32("enable_shaders") != 0) c = MapBlock_LightColor(255, 0xffff, decode_light(f.light_source)); setMeshColor(node_mesh, c); /* Scale and translate the mesh so it's a unit cube centered on the origin */ scaleMesh(node_mesh, v3f(1.0/BS, 1.0/BS, 1.0/BS)); translateMesh(node_mesh, v3f(-1.0, -1.0, -1.0)); /* Draw node mesh into a render target texture */ if(cc->inventory_texture == NULL) { core::dimension2d<u32> dim(64,64); std::string rtt_texture_name = "INVENTORY_" + def->name + "_RTT"; v3f camera_position(0, 1.0, -1.5); camera_position.rotateXZBy(45); v3f camera_lookat(0, 0, 0); core::CMatrix4<f32> camera_projection_matrix; // Set orthogonal projection camera_projection_matrix.buildProjectionMatrixOrthoLH( 1.65, 1.65, 0, 100); video::SColorf ambient_light(0.2,0.2,0.2); v3f light_position(10, 100, -50); video::SColorf light_color(0.5,0.5,0.5); f32 light_radius = 1000; cc->inventory_texture = generateTextureFromMesh( node_mesh, device, dim, rtt_texture_name, camera_position, camera_lookat, camera_projection_matrix, ambient_light, light_position, light_color, light_radius); // render-to-target didn't work if(cc->inventory_texture == NULL) { cc->inventory_texture = tsrc->getTextureRaw(f.tiledef[0].name); } } else { if (m_driver == 0) m_driver = driver; m_extruded_textures.push_back(cc->inventory_texture); } /* Use the node mesh as the wield mesh */ // Scale to proper wield mesh proportions scaleMesh(node_mesh, v3f(30.0, 30.0, 30.0) * def->wield_scale); cc->wield_mesh = node_mesh; cc->wield_mesh->grab(); //no way reference count can be smaller than 2 in this place! assert(cc->wield_mesh->getReferenceCount() >= 2); } // Put in cache m_clientcached.set(name, cc); return cc; }
void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client) { 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); if (m_enable_shaders) { 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 != "") { setExtruded(def.wield_image, def.wield_scale, tsrc, 1); return; } // Handle nodes // See also CItemDefManager::createClientCached() else if (def.type == ITEM_NODE) { if (f.mesh_ptr[0]) { // e.g. mesh nodes and nodeboxes changeToMesh(f.mesh_ptr[0]); // mesh_ptr[0] is pre-scaled by BS * f->visual_scale m_meshnode->setScale( def.wield_scale * WIELD_SCALE_FACTOR / (BS * f.visual_scale)); } else if (f.drawtype == NDT_AIRLIKE) { changeToMesh(NULL); } else if (f.drawtype == NDT_PLANTLIKE) { setExtruded(tsrc->getTextureName(f.tiles[0].texture_id), def.wield_scale, tsrc, f.tiles[0].animation_frame_count); } else if (f.drawtype == NDT_NORMAL || f.drawtype == NDT_ALLFACES) { setCube(f.tiles, def.wield_scale, tsrc); } else { 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)); changeToMesh(mapblock_mesh.getMesh()); translateMesh(m_meshnode->getMesh(), v3f(-BS, -BS, -BS)); m_meshnode->setScale( def.wield_scale * WIELD_SCALE_FACTOR / (BS * f.visual_scale)); } u32 material_count = m_meshnode->getMaterialCount(); if (material_count > 6) { errorstream << "WieldMeshSceneNode::setItem: Invalid material " "count " << material_count << ", truncating to 6" << std::endl; 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 = (tile->animation_frame_count > 1); if (animated) { FrameSpec animation_frame = tile->frames[0]; material.setTexture(0, animation_frame.texture); } else { material.setTexture(0, tile->texture); } m_colors.push_back(tile->color); material.MaterialType = m_material_type; if (m_enable_shaders) { if (tile->normal_texture) { if (animated) { FrameSpec animation_frame = tile->frames[0]; material.setTexture(1, animation_frame.normal_texture); } else { material.setTexture(1, tile->normal_texture); } } material.setTexture(2, tile->flags_texture); } } return; } else if (def.inventory_image != "") { setExtruded(def.inventory_image, def.wield_scale, tsrc, 1); return; } // no wield mesh found changeToMesh(NULL); }
scene::IMesh *getItemMesh(Client *client, const ItemStack &item) { 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); if (!g_extrusion_mesh_cache) { g_extrusion_mesh_cache = new ExtrusionMeshCache(); } else { g_extrusion_mesh_cache->grab(); } scene::IMesh *mesh; // If inventory_image is defined, it overrides everything else if (def.inventory_image != "") { mesh = getExtrudedMesh(tsrc, def.inventory_image); return mesh; } else if (def.type == ITEM_NODE) { if (f.mesh_ptr[0]) { mesh = cloneMesh(f.mesh_ptr[0]); scaleMesh(mesh, v3f(0.12, 0.12, 0.12)); setMeshColor(mesh, video::SColor (255, 255, 255, 255)); } else if (f.drawtype == NDT_PLANTLIKE) { mesh = getExtrudedMesh(tsrc, tsrc->getTextureName(f.tiles[0].texture_id)); } 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()); scaleMesh(mesh, v3f(1.2, 1.2, 1.2)); } else { 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)); mesh = cloneMesh(mapblock_mesh.getMesh()); translateMesh(mesh, v3f(-BS, -BS, -BS)); scaleMesh(mesh, v3f(0.12, 0.12, 0.12)); u32 mc = mesh->getMeshBufferCount(); for (u32 i = 0; i < mc; ++i) { video::SMaterial &material1 = mesh->getMeshBuffer(i)->getMaterial(); video::SMaterial &material2 = mapblock_mesh.getMesh()->getMeshBuffer(i)->getMaterial(); material1.setTexture(0, material2.getTexture(0)); material1.setTexture(1, material2.getTexture(1)); material1.setTexture(2, material2.getTexture(2)); material1.setTexture(3, material2.getTexture(3)); material1.MaterialType = material2.MaterialType; } } u32 mc = mesh->getMeshBufferCount(); for (u32 i = 0; i < mc; ++i) { 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 (tile->animation_frame_count > 1) { FrameSpec animation_frame = tile->frames[0]; material.setTexture(0, animation_frame.texture); } else { material.setTexture(0, tile->texture); } } rotateMeshXZby(mesh, -45); rotateMeshYZby(mesh, -30); return mesh; } return NULL; }
void mapblock_mesh_generate_special(MeshMakeData *data, MeshCollector &collector) { INodeDefManager *nodedef = data->m_gamedef->ndef(); // 0ms //TimeTaker timer("mapblock_mesh_generate_special()"); /* Some settings */ bool new_style_water = g_settings->getBool("new_style_water"); float node_liquid_level = 1.0; if(new_style_water) node_liquid_level = 0.85; v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE; for(s16 z=0; z<MAP_BLOCKSIZE; z++) for(s16 y=0; y<MAP_BLOCKSIZE; y++) for(s16 x=0; x<MAP_BLOCKSIZE; x++) { v3s16 p(x,y,z); MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes+p); const ContentFeatures &f = nodedef->get(n); // Only solidness=0 stuff is drawn here if(f.solidness != 0) continue; switch(f.drawtype){ default: infostream<<"Got "<<f.drawtype<<std::endl; assert(0); break; case NDT_AIRLIKE: break; case NDT_LIQUID: { /* Add water sources to mesh if using new style */ TileSpec tile_liquid = f.special_tiles[0]; AtlasPointer &pa_liquid = tile_liquid.texture; bool top_is_air = false; MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z)); if(n.getContent() == CONTENT_AIR) top_is_air = true; if(top_is_air == false) continue; u16 l = getInteriorLight(n, 0, data); video::SColor c = MapBlock_LightColor(f.alpha, l); video::S3DVertex vertices[4] = { video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, pa_liquid.x0(), pa_liquid.y1()), video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, pa_liquid.x1(), pa_liquid.y1()), video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, pa_liquid.x1(), pa_liquid.y0()), video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, pa_liquid.x0(), pa_liquid.y0()), }; v3f offset(p.X, p.Y + (-0.5+node_liquid_level)*BS, p.Z); for(s32 i=0; i<4; i++) { vertices[i].Pos += offset; } u16 indices[] = {0,1,2,2,3,0}; // Add to mesh collector collector.append(tile_liquid, vertices, 4, indices, 6); break;} case NDT_FLOWINGLIQUID: { /* Add flowing liquid to mesh */ TileSpec tile_liquid = f.special_tiles[0]; TileSpec tile_liquid_bfculled = f.special_tiles[1]; AtlasPointer &pa_liquid = tile_liquid.texture; bool top_is_same_liquid = false; MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z)); content_t c_flowing = nodedef->getId(f.liquid_alternative_flowing); content_t c_source = nodedef->getId(f.liquid_alternative_source); if(ntop.getContent() == c_flowing || ntop.getContent() == c_source) top_is_same_liquid = true; u16 l = 0; // If this liquid emits light and doesn't contain light, draw // it at what it emits, for an increased effect u8 light_source = nodedef->get(n).light_source; if(light_source != 0){ //l = decode_light(undiminish_light(light_source)); l = decode_light(light_source); l = l | (l<<8); } // Use the light of the node on top if possible else if(nodedef->get(ntop).param_type == CPT_LIGHT) l = getInteriorLight(ntop, 0, data); // Otherwise use the light of this node (the liquid) else l = getInteriorLight(n, 0, data); video::SColor c = MapBlock_LightColor(f.alpha, l); // Neighbor liquid levels (key = relative position) // Includes current node core::map<v3s16, f32> neighbor_levels; core::map<v3s16, content_t> neighbor_contents; core::map<v3s16, u8> neighbor_flags; const u8 neighborflag_top_is_same_liquid = 0x01; v3s16 neighbor_dirs[9] = { v3s16(0,0,0), v3s16(0,0,1), v3s16(0,0,-1), v3s16(1,0,0), v3s16(-1,0,0), v3s16(1,0,1), v3s16(-1,0,-1), v3s16(1,0,-1), v3s16(-1,0,1), }; for(u32 i=0; i<9; i++) { content_t content = CONTENT_AIR; float level = -0.5 * BS; u8 flags = 0; // Check neighbor v3s16 p2 = p + neighbor_dirs[i]; MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2); if(n2.getContent() != CONTENT_IGNORE) { content = n2.getContent(); if(n2.getContent() == c_source) level = (-0.5+node_liquid_level) * BS; else if(n2.getContent() == c_flowing) level = (-0.5 + ((float)(n2.param2&LIQUID_LEVEL_MASK) + 0.5) / 8.0 * node_liquid_level) * BS; // Check node above neighbor. // NOTE: This doesn't get executed if neighbor // doesn't exist p2.Y += 1; n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2); if(n2.getContent() == c_source || n2.getContent() == c_flowing) flags |= neighborflag_top_is_same_liquid; } neighbor_levels.insert(neighbor_dirs[i], level); neighbor_contents.insert(neighbor_dirs[i], content); neighbor_flags.insert(neighbor_dirs[i], flags); } // Corner heights (average between four liquids) f32 corner_levels[4]; v3s16 halfdirs[4] = { v3s16(0,0,0), v3s16(1,0,0), v3s16(1,0,1), v3s16(0,0,1), }; for(u32 i=0; i<4; i++) { v3s16 cornerdir = halfdirs[i]; float cornerlevel = 0; u32 valid_count = 0; u32 air_count = 0; for(u32 j=0; j<4; j++) { v3s16 neighbordir = cornerdir - halfdirs[j]; content_t content = neighbor_contents[neighbordir]; // If top is liquid, draw starting from top of node if(neighbor_flags[neighbordir] & neighborflag_top_is_same_liquid) { cornerlevel = 0.5*BS; valid_count = 1; break; } // Source is always the same height else if(content == c_source) { cornerlevel = (-0.5+node_liquid_level)*BS; valid_count = 1; break; } // Flowing liquid has level information else if(content == c_flowing) { cornerlevel += neighbor_levels[neighbordir]; valid_count++; } else if(content == CONTENT_AIR) { air_count++; } } if(air_count >= 2) cornerlevel = -0.5*BS; else if(valid_count > 0) cornerlevel /= valid_count; corner_levels[i] = cornerlevel; } /* Generate sides */ v3s16 side_dirs[4] = { v3s16(1,0,0), v3s16(-1,0,0), v3s16(0,0,1), v3s16(0,0,-1), }; s16 side_corners[4][2] = { {1, 2}, {3, 0}, {2, 3}, {0, 1}, }; for(u32 i=0; i<4; i++) { v3s16 dir = side_dirs[i]; /* If our topside is liquid and neighbor's topside is liquid, don't draw side face */ if(top_is_same_liquid && neighbor_flags[dir] & neighborflag_top_is_same_liquid) continue; content_t neighbor_content = neighbor_contents[dir]; const ContentFeatures &n_feat = nodedef->get(neighbor_content); // Don't draw face if neighbor is blocking the view if(n_feat.solidness == 2) continue; bool neighbor_is_same_liquid = (neighbor_content == c_source || neighbor_content == c_flowing); // Don't draw any faces if neighbor same is liquid and top is // same liquid if(neighbor_is_same_liquid == true && top_is_same_liquid == false) continue; // 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) current_tile = &tile_liquid_bfculled; video::S3DVertex vertices[4] = { video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, pa_liquid.x0(), pa_liquid.y1()), video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, pa_liquid.x1(), pa_liquid.y1()), video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, pa_liquid.x1(), pa_liquid.y0()), video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, pa_liquid.x0(), pa_liquid.y0()), }; /* If our topside is liquid, set upper border of face at upper border of node */ if(top_is_same_liquid) { vertices[2].Pos.Y = 0.5*BS; vertices[3].Pos.Y = 0.5*BS; } /* Otherwise upper position of face is corner levels */ else { vertices[2].Pos.Y = corner_levels[side_corners[i][0]]; vertices[3].Pos.Y = corner_levels[side_corners[i][1]]; } /* If neighbor is liquid, lower border of face is corner liquid levels */ if(neighbor_is_same_liquid) { vertices[0].Pos.Y = corner_levels[side_corners[i][1]]; vertices[1].Pos.Y = corner_levels[side_corners[i][0]]; } /* If neighbor is not liquid, lower border of face is lower border of node */ else { vertices[0].Pos.Y = -0.5*BS; vertices[1].Pos.Y = -0.5*BS; } for(s32 j=0; j<4; j++) { if(dir == v3s16(0,0,1)) vertices[j].Pos.rotateXZBy(0); if(dir == v3s16(0,0,-1)) vertices[j].Pos.rotateXZBy(180); if(dir == v3s16(-1,0,0)) vertices[j].Pos.rotateXZBy(90); if(dir == v3s16(1,0,-0)) vertices[j].Pos.rotateXZBy(-90); // Do this to not cause glitches when two liquids are // side-by-side /*if(neighbor_is_same_liquid == false){ vertices[j].Pos.X *= 0.98; vertices[j].Pos.Z *= 0.98; }*/ vertices[j].Pos += intToFloat(p, BS); } u16 indices[] = {0,1,2,2,3,0}; // Add to mesh collector collector.append(*current_tile, vertices, 4, indices, 6); } /* Generate top side, if appropriate */ if(top_is_same_liquid == false) { video::S3DVertex vertices[4] = { video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, pa_liquid.x0(), pa_liquid.y1()), video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, pa_liquid.x1(), pa_liquid.y1()), video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, pa_liquid.x1(), pa_liquid.y0()), video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, pa_liquid.x0(), pa_liquid.y0()), }; // To get backface culling right, the vertices need to go // clockwise around the front of the face. And we happened to // calculate corner levels in exact reverse order. s32 corner_resolve[4] = {3,2,1,0}; for(s32 i=0; i<4; i++) { //vertices[i].Pos.Y += liquid_level; //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)]; s32 j = corner_resolve[i]; vertices[i].Pos.Y += corner_levels[j]; vertices[i].Pos += intToFloat(p, BS); } // Default downwards-flowing texture animation goes from // -Z towards +Z, thus the direction is +Z. // Rotate texture to make animation go in flow direction // Positive if liquid moves towards +Z int dz = (corner_levels[side_corners[2][0]] + corner_levels[side_corners[2][1]] < corner_levels[side_corners[3][0]] + corner_levels[side_corners[3][1]]); // Positive if liquid moves towards +X int dx = (corner_levels[side_corners[0][0]] + corner_levels[side_corners[0][1]] < corner_levels[side_corners[1][0]] + corner_levels[side_corners[1][1]]); // -X if(-dx >= abs(dz)) { v2f t = vertices[0].TCoords; vertices[0].TCoords = vertices[1].TCoords; vertices[1].TCoords = vertices[2].TCoords; vertices[2].TCoords = vertices[3].TCoords; vertices[3].TCoords = t; } // +X if(dx >= abs(dz)) { v2f t = vertices[0].TCoords; vertices[0].TCoords = vertices[3].TCoords; vertices[3].TCoords = vertices[2].TCoords; vertices[2].TCoords = vertices[1].TCoords; vertices[1].TCoords = t; } // -Z if(-dz >= abs(dx)) { v2f t = vertices[0].TCoords; vertices[0].TCoords = vertices[3].TCoords; vertices[3].TCoords = vertices[2].TCoords; vertices[2].TCoords = vertices[1].TCoords; vertices[1].TCoords = t; t = vertices[0].TCoords; vertices[0].TCoords = vertices[3].TCoords; vertices[3].TCoords = vertices[2].TCoords; vertices[2].TCoords = vertices[1].TCoords; vertices[1].TCoords = t; } u16 indices[] = {0,1,2,2,3,0}; // Add to mesh collector collector.append(tile_liquid, vertices, 4, indices, 6); } break;} case NDT_GLASSLIKE: { TileSpec tile = getNodeTile(n, p, v3s16(0,0,0), data); AtlasPointer ap = tile.texture; u16 l = getInteriorLight(n, 1, data); video::SColor c = MapBlock_LightColor(255, l); for(u32 j=0; j<6; j++) { // Check this neighbor v3s16 n2p = blockpos_nodes + p + g_6dirs[j]; MapNode n2 = data->m_vmanip.getNodeNoEx(n2p); // Don't make face if neighbor is of same type if(n2.getContent() == n.getContent()) continue; // The face at Z+ video::S3DVertex vertices[4] = { video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, ap.x0(), ap.y1()), video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, ap.x1(), ap.y1()), video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, ap.x1(), ap.y0()), video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, ap.x0(), ap.y0()), }; // Rotations in the g_6dirs format if(j == 0) // Z+ for(u16 i=0; i<4; i++) vertices[i].Pos.rotateXZBy(0); else if(j == 1) // Y+ for(u16 i=0; i<4; i++) vertices[i].Pos.rotateYZBy(-90); else if(j == 2) // X+ for(u16 i=0; i<4; i++) vertices[i].Pos.rotateXZBy(-90); else if(j == 3) // Z- for(u16 i=0; i<4; i++) vertices[i].Pos.rotateXZBy(180); else if(j == 4) // Y- for(u16 i=0; i<4; i++) vertices[i].Pos.rotateYZBy(90); else if(j == 5) // X- for(u16 i=0; i<4; i++) vertices[i].Pos.rotateXZBy(90); for(u16 i=0; i<4; i++){ vertices[i].Pos += intToFloat(p, BS); } u16 indices[] = {0,1,2,2,3,0}; // Add to mesh collector collector.append(tile, vertices, 4, indices, 6); } break;} case NDT_ALLFACES: { TileSpec tile_leaves = getNodeTile(n, p, v3s16(0,0,0), data); AtlasPointer pa_leaves = tile_leaves.texture; u16 l = getInteriorLight(n, 1, data); video::SColor c = MapBlock_LightColor(255, l); 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); break;} case NDT_ALLFACES_OPTIONAL: // This is always pre-converted to something else assert(0); break; case NDT_TORCHLIKE: { v3s16 dir = n.getWallMountedDir(nodedef); u8 tileindex = 0; if(dir == v3s16(0,-1,0)){ tileindex = 0; // floor } else if(dir == v3s16(0,1,0)){ tileindex = 1; // ceiling // For backwards compatibility } else if(dir == v3s16(0,0,0)){ tileindex = 0; // floor } else { tileindex = 2; // side } TileSpec tile = getNodeTileN(n, p, tileindex, data); tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING; tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY; AtlasPointer ap = tile.texture; video::SColor c(255,255,255,255); // Wall at X+ of node video::S3DVertex vertices[4] = { video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, ap.x0(), ap.y1()), video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, ap.x1(), ap.y1()), video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, ap.x1(), ap.y0()), video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, ap.x0(), ap.y0()), }; for(s32 i=0; i<4; i++) { if(dir == v3s16(1,0,0)) vertices[i].Pos.rotateXZBy(0); if(dir == v3s16(-1,0,0)) vertices[i].Pos.rotateXZBy(180); if(dir == v3s16(0,0,1)) vertices[i].Pos.rotateXZBy(90); if(dir == v3s16(0,0,-1)) vertices[i].Pos.rotateXZBy(-90); if(dir == v3s16(0,-1,0)) vertices[i].Pos.rotateXZBy(45); if(dir == v3s16(0,1,0)) vertices[i].Pos.rotateXZBy(-45); vertices[i].Pos += intToFloat(p, BS); } u16 indices[] = {0,1,2,2,3,0}; // Add to mesh collector collector.append(tile, vertices, 4, indices, 6); break;} case NDT_SIGNLIKE: { TileSpec tile = getNodeTileN(n, p, 0, data); tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING; tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY; AtlasPointer ap = tile.texture; u16 l = getInteriorLight(n, 0, data); video::SColor c = MapBlock_LightColor(255, l); float d = (float)BS/16; // Wall at X+ of node video::S3DVertex vertices[4] = { video::S3DVertex(BS/2-d,BS/2,BS/2, 0,0,0, c, ap.x0(), ap.y0()), video::S3DVertex(BS/2-d,BS/2,-BS/2, 0,0,0, c, ap.x1(), ap.y0()), video::S3DVertex(BS/2-d,-BS/2,-BS/2, 0,0,0, c, ap.x1(), ap.y1()), video::S3DVertex(BS/2-d,-BS/2,BS/2, 0,0,0, c, ap.x0(), ap.y1()), }; v3s16 dir = n.getWallMountedDir(nodedef); for(s32 i=0; i<4; i++) { if(dir == v3s16(1,0,0)) vertices[i].Pos.rotateXZBy(0); if(dir == v3s16(-1,0,0)) vertices[i].Pos.rotateXZBy(180); if(dir == v3s16(0,0,1)) vertices[i].Pos.rotateXZBy(90); if(dir == v3s16(0,0,-1)) vertices[i].Pos.rotateXZBy(-90); if(dir == v3s16(0,-1,0)) vertices[i].Pos.rotateXYBy(-90); if(dir == v3s16(0,1,0)) vertices[i].Pos.rotateXYBy(90); vertices[i].Pos += intToFloat(p, BS); } u16 indices[] = {0,1,2,2,3,0}; // Add to mesh collector collector.append(tile, vertices, 4, indices, 6); break;} case NDT_PLANTLIKE: { TileSpec tile = getNodeTileN(n, p, 0, data); tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY; AtlasPointer ap = tile.texture; u16 l = getInteriorLight(n, 1, data); video::SColor c = MapBlock_LightColor(255, l); for(u32 j=0; j<4; j++) { video::S3DVertex vertices[4] = { video::S3DVertex(-BS/2*f.visual_scale,-BS/2,0, 0,0,0, c, ap.x0(), ap.y1()), video::S3DVertex( BS/2*f.visual_scale,-BS/2,0, 0,0,0, c, ap.x1(), ap.y1()), video::S3DVertex( BS/2*f.visual_scale, -BS/2 + f.visual_scale*BS,0, 0,0,0, c, ap.x1(), ap.y0()), video::S3DVertex(-BS/2*f.visual_scale, -BS/2 + f.visual_scale*BS,0, 0,0,0, c, ap.x0(), ap.y0()), }; if(j == 0) { for(u16 i=0; i<4; i++) vertices[i].Pos.rotateXZBy(45); } else if(j == 1) { for(u16 i=0; i<4; i++) vertices[i].Pos.rotateXZBy(-45); } else if(j == 2) { for(u16 i=0; i<4; i++) vertices[i].Pos.rotateXZBy(135); } else if(j == 3) { for(u16 i=0; i<4; i++) vertices[i].Pos.rotateXZBy(-135); } for(u16 i=0; i<4; i++) { vertices[i].Pos *= f.visual_scale; vertices[i].Pos += intToFloat(p, BS); } u16 indices[] = {0,1,2,2,3,0}; // Add to mesh collector collector.append(tile, vertices, 4, indices, 6); } break;} case NDT_FENCELIKE: { TileSpec tile = getNodeTile(n, p, v3s16(0,0,0), data); TileSpec tile_nocrack = tile; tile_nocrack.material_flags &= ~MATERIAL_FLAG_CRACK; // A hack to put wood the right way around in the posts ITextureSource *tsrc = data->m_gamedef->tsrc(); TileSpec tile_rot = tile; tile_rot.texture = tsrc->getTexture(tsrc->getTextureName( tile.texture.id) + "^[transformR90"); u16 l = getInteriorLight(n, 1, data); video::SColor c = MapBlock_LightColor(255, l); const f32 post_rad=(f32)BS/8; const f32 bar_rad=(f32)BS/16; const f32 bar_len=(f32)(BS/2)-post_rad; v3f pos = intToFloat(p, BS); // The post - always present aabb3f post(-post_rad,-BS/2,-post_rad,post_rad,BS/2,post_rad); post.MinEdge += pos; post.MaxEdge += pos; f32 postuv[24]={ 6/16.,6/16.,10/16.,10/16., 6/16.,6/16.,10/16.,10/16., 0/16.,0,4/16.,1, 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); // Now a section of fence, +X, if there's a post there v3s16 p2 = p; p2.X++; MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2); const ContentFeatures *f2 = &nodedef->get(n2); if(f2->drawtype == NDT_FENCELIKE) { aabb3f bar(-bar_len+BS/2,-bar_rad+BS/4,-bar_rad, bar_len+BS/2,bar_rad+BS/4,bar_rad); bar.MinEdge += pos; bar.MaxEdge += pos; f32 xrailuv[24]={ 0/16.,2/16.,16/16.,4/16., 0/16.,4/16.,16/16.,6/16., 6/16.,6/16.,8/16.,8/16., 10/16.,10/16.,12/16.,12/16., 0/16.,8/16.,16/16.,10/16., 0/16.,14/16.,16/16.,16/16.}; makeCuboid(&collector, bar, &tile_nocrack, 1, c, xrailuv); bar.MinEdge.Y -= BS/2; bar.MaxEdge.Y -= BS/2; makeCuboid(&collector, bar, &tile_nocrack, 1, c, xrailuv); } // Now a section of fence, +Z, if there's a post there p2 = p; p2.Z++; n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2); f2 = &nodedef->get(n2); if(f2->drawtype == NDT_FENCELIKE) { aabb3f bar(-bar_rad,-bar_rad+BS/4,-bar_len+BS/2, bar_rad,bar_rad+BS/4,bar_len+BS/2); bar.MinEdge += pos; bar.MaxEdge += pos; f32 zrailuv[24]={ 3/16.,1/16.,5/16.,5/16., // cannot rotate; stretch 4/16.,1/16.,6/16.,5/16., // for wood texture instead 0/16.,9/16.,16/16.,11/16., 0/16.,6/16.,16/16.,8/16., 6/16.,6/16.,8/16.,8/16., 10/16.,10/16.,12/16.,12/16.}; makeCuboid(&collector, bar, &tile_nocrack, 1, c, zrailuv); bar.MinEdge.Y -= BS/2; bar.MaxEdge.Y -= BS/2; makeCuboid(&collector, bar, &tile_nocrack, 1, c, zrailuv); } break;} case NDT_RAILLIKE: { bool is_rail_x [] = { false, false }; /* x-1, x+1 */ bool is_rail_z [] = { false, false }; /* z-1, z+1 */ bool is_rail_z_minus_y [] = { false, false }; /* z-1, z+1; y-1 */ bool is_rail_x_minus_y [] = { false, false }; /* x-1, z+1; y-1 */ bool is_rail_z_plus_y [] = { false, false }; /* z-1, z+1; y+1 */ bool is_rail_x_plus_y [] = { false, false }; /* x-1, x+1; y+1 */ MapNode n_minus_x = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x-1,y,z)); MapNode n_plus_x = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x+1,y,z)); MapNode n_minus_z = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y,z-1)); MapNode n_plus_z = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y,z+1)); MapNode n_plus_x_plus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x+1, y+1, z)); MapNode n_plus_x_minus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x+1, y-1, z)); MapNode n_minus_x_plus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x-1, y+1, z)); MapNode n_minus_x_minus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x-1, y-1, z)); MapNode n_plus_z_plus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x, y+1, z+1)); MapNode n_minus_z_plus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x, y+1, z-1)); MapNode n_plus_z_minus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x, y-1, z+1)); MapNode n_minus_z_minus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x, y-1, z-1)); content_t thiscontent = n.getContent(); if(n_minus_x.getContent() == thiscontent) is_rail_x[0] = true; if (n_minus_x_minus_y.getContent() == thiscontent) is_rail_x_minus_y[0] = true; if(n_minus_x_plus_y.getContent() == thiscontent) is_rail_x_plus_y[0] = true; if(n_plus_x.getContent() == thiscontent) is_rail_x[1] = true; if (n_plus_x_minus_y.getContent() == thiscontent) is_rail_x_minus_y[1] = true; if(n_plus_x_plus_y.getContent() == thiscontent) is_rail_x_plus_y[1] = true; if(n_minus_z.getContent() == thiscontent) is_rail_z[0] = true; if (n_minus_z_minus_y.getContent() == thiscontent) is_rail_z_minus_y[0] = true; if(n_minus_z_plus_y.getContent() == thiscontent) is_rail_z_plus_y[0] = true; if(n_plus_z.getContent() == thiscontent) is_rail_z[1] = true; if (n_plus_z_minus_y.getContent() == thiscontent) is_rail_z_minus_y[1] = true; if(n_plus_z_plus_y.getContent() == thiscontent) is_rail_z_plus_y[1] = true; bool is_rail_x_all[] = {false, false}; bool is_rail_z_all[] = {false, false}; is_rail_x_all[0]=is_rail_x[0] || is_rail_x_minus_y[0] || is_rail_x_plus_y[0]; is_rail_x_all[1]=is_rail_x[1] || is_rail_x_minus_y[1] || is_rail_x_plus_y[1]; is_rail_z_all[0]=is_rail_z[0] || is_rail_z_minus_y[0] || is_rail_z_plus_y[0]; is_rail_z_all[1]=is_rail_z[1] || is_rail_z_minus_y[1] || is_rail_z_plus_y[1]; // reasonable default, flat straight unrotated rail bool is_straight = true; int adjacencies = 0; int angle = 0; u8 tileindex = 0; // check for sloped rail if (is_rail_x_plus_y[0] || is_rail_x_plus_y[1] || is_rail_z_plus_y[0] || is_rail_z_plus_y[1]) { adjacencies = 5; //5 means sloped is_straight = true; // sloped is always straight } else { // is really straight, rails on both sides is_straight = (is_rail_x_all[0] && is_rail_x_all[1]) || (is_rail_z_all[0] && is_rail_z_all[1]); adjacencies = is_rail_x_all[0] + is_rail_x_all[1] + is_rail_z_all[0] + is_rail_z_all[1]; } switch (adjacencies) { case 1: if(is_rail_x_all[0] || is_rail_x_all[1]) angle = 90; break; case 2: if(!is_straight) tileindex = 1; // curved if(is_rail_x_all[0] && is_rail_x_all[1]) angle = 90; if(is_rail_z_all[0] && is_rail_z_all[1]){ if (n_minus_z_plus_y.getContent() == thiscontent) angle = 180; } else if(is_rail_x_all[0] && is_rail_z_all[0]) angle = 270; else if(is_rail_x_all[0] && is_rail_z_all[1]) angle = 180; else if(is_rail_x_all[1] && is_rail_z_all[1]) angle = 90; break; case 3: // here is where the potential to 'switch' a junction is, but not implemented at present tileindex = 2; // t-junction if(!is_rail_x_all[1]) angle=180; if(!is_rail_z_all[0]) angle=90; if(!is_rail_z_all[1]) angle=270; break; case 4: tileindex = 3; // crossing break; case 5: //sloped if(is_rail_z_plus_y[0]) angle = 180; if(is_rail_x_plus_y[0]) angle = 90; if(is_rail_x_plus_y[1]) angle = -90; break; default: break; } TileSpec tile = getNodeTileN(n, p, tileindex, data); tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING; tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY; AtlasPointer ap = tile.texture; u16 l = getInteriorLight(n, 0, data); video::SColor c = MapBlock_LightColor(255, l); float d = (float)BS/64; char g=-1; if (is_rail_x_plus_y[0] || is_rail_x_plus_y[1] || is_rail_z_plus_y[0] || is_rail_z_plus_y[1]) g=1; //Object is at a slope video::S3DVertex vertices[4] = { video::S3DVertex(-BS/2,-BS/2+d,-BS/2, 0,0,0, c, ap.x0(), ap.y1()), video::S3DVertex(BS/2,-BS/2+d,-BS/2, 0,0,0, c, ap.x1(), ap.y1()), video::S3DVertex(BS/2,g*BS/2+d,BS/2, 0,0,0, c, ap.x1(), ap.y0()), video::S3DVertex(-BS/2,g*BS/2+d,BS/2, 0,0,0, c, ap.x0(), ap.y0()), }; for(s32 i=0; i<4; i++) { if(angle != 0) vertices[i].Pos.rotateXZBy(angle); vertices[i].Pos += intToFloat(p, BS); } u16 indices[] = {0,1,2,2,3,0}; collector.append(tile, vertices, 4, indices, 6); break;} case NDT_NODEBOX: { TileSpec tiles[6]; for(int i = 0; i < 6; i++) { tiles[i] = getNodeTileN(n, p, i, data); } u16 l = getInteriorLight(n, 0, data); video::SColor c = MapBlock_LightColor(255, l); v3f pos = intToFloat(p, BS); std::vector<aabb3f> boxes = n.getNodeBoxes(nodedef); for(std::vector<aabb3f>::iterator i = boxes.begin(); i != boxes.end(); i++) { aabb3f box = *i; box.MinEdge += pos; box.MaxEdge += pos; // Compute texture coords f32 tx1 = (i->MinEdge.X/BS)+0.5; f32 ty1 = (i->MinEdge.Y/BS)+0.5; f32 tz1 = (i->MinEdge.Z/BS)+0.5; f32 tx2 = (i->MaxEdge.X/BS)+0.5; f32 ty2 = (i->MaxEdge.Y/BS)+0.5; f32 tz2 = (i->MaxEdge.Z/BS)+0.5; f32 txc[24] = { // up tx1, 1-tz2, tx2, 1-tz1, // down tx1, tz1, tx2, tz2, // right tz1, 1-ty2, tz2, 1-ty1, // left 1-tz2, 1-ty2, 1-tz1, 1-ty1, // back 1-tx2, 1-ty2, 1-tx1, 1-ty1, // front tx1, 1-ty2, tx2, 1-ty1, }; makeCuboid(&collector, box, tiles, 6, c, txc); } break;} } } }
void CNodeDefManager::updateTextures(IGameDef *gamedef, void (*progress_callback)(void *progress_args, u32 progress, u32 max_progress), void *progress_callback_args) { #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(); scene::IMeshManipulator* meshmanip = smgr->getMeshManipulator(); bool new_style_water = g_settings->getBool("new_style_water"); bool connected_glass = g_settings->getBool("connected_glass"); bool opaque_water = g_settings->getBool("opaque_water"); bool enable_shaders = g_settings->getBool("enable_shaders"); bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping"); bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion"); bool enable_mesh_cache = g_settings->getBool("enable_mesh_cache"); bool enable_minimap = g_settings->getBool("enable_minimap"); std::string leaves_style = g_settings->get("leaves_style"); bool use_normal_texture = enable_shaders && (enable_bumpmapping || enable_parallax_occlusion); u32 size = m_content_features.size(); for (u32 i = 0; i < size; i++) { ContentFeatures *f = &m_content_features[i]; // minimap pixel color - the average color of a texture if (enable_minimap && f->tiledef[0].name != "") f->minimap_color = tsrc->getTextureAverageColor(f->tiledef[0].name); // Figure out the actual tiles to use TileDef tiledef[6]; for (u32 j = 0; j < 6; j++) { tiledef[j] = f->tiledef[j]; if (tiledef[j].name == "") tiledef[j].name = "unknown_node.png"; } bool is_liquid = false; bool is_water_surface = false; u8 material_type = (f->alpha == 255) ? TILE_MATERIAL_BASIC : TILE_MATERIAL_ALPHA; switch (f->drawtype) { default: case NDT_NORMAL: f->solidness = 2; break; case NDT_AIRLIKE: f->solidness = 0; break; case NDT_LIQUID: assert(f->liquid_type == LIQUID_SOURCE); if (opaque_water) f->alpha = 255; f->solidness = new_style_water ? 0 : 1; is_liquid = true; break; case NDT_FLOWINGLIQUID: assert(f->liquid_type == LIQUID_FLOWING); f->solidness = 0; if (opaque_water) f->alpha = 255; is_liquid = true; break; case NDT_GLASSLIKE: f->solidness = 0; f->visual_solidness = 1; break; case NDT_GLASSLIKE_FRAMED: f->solidness = 0; f->visual_solidness = 1; break; case NDT_GLASSLIKE_FRAMED_OPTIONAL: f->solidness = 0; f->visual_solidness = 1; f->drawtype = connected_glass ? NDT_GLASSLIKE_FRAMED : NDT_GLASSLIKE; break; case NDT_ALLFACES: f->solidness = 0; f->visual_solidness = 1; break; case NDT_ALLFACES_OPTIONAL: if (leaves_style == "fancy") { f->drawtype = NDT_ALLFACES; f->solidness = 0; f->visual_solidness = 1; } else if (leaves_style == "simple") { for (u32 j = 0; j < 6; j++) { if (f->tiledef_special[j].name != "") tiledef[j].name = f->tiledef_special[j].name; } f->drawtype = NDT_GLASSLIKE; f->solidness = 0; f->visual_solidness = 1; } else { f->drawtype = NDT_NORMAL; f->solidness = 2; for (u32 i = 0; i < 6; i++) tiledef[i].name += std::string("^[noalpha"); } if (f->waving == 1) material_type = TILE_MATERIAL_WAVING_LEAVES; break; case NDT_PLANTLIKE: f->solidness = 0; if (f->waving == 1) material_type = TILE_MATERIAL_WAVING_PLANTS; break; case NDT_FIRELIKE: f->solidness = 0; break; case NDT_MESH: f->solidness = 0; break; case NDT_TORCHLIKE: case NDT_SIGNLIKE: case NDT_FENCELIKE: case NDT_RAILLIKE: case NDT_NODEBOX: f->solidness = 0; break; } if (is_liquid) { material_type = (f->alpha == 255) ? TILE_MATERIAL_LIQUID_OPAQUE : TILE_MATERIAL_LIQUID_TRANSPARENT; if (f->name == "default:water_source") is_water_surface = true; } u32 tile_shader[6]; for (u16 j = 0; j < 6; j++) { tile_shader[j] = shdsrc->getShader("nodes_shader", material_type, f->drawtype); } if (is_water_surface) { tile_shader[0] = shdsrc->getShader("water_surface_shader", material_type, f->drawtype); } // Tiles (fill in f->tiles[]) for (u16 j = 0; j < 6; j++) { fillTileAttribs(tsrc, &f->tiles[j], &tiledef[j], tile_shader[j], use_normal_texture, f->tiledef[j].backface_culling, f->alpha, material_type); } // Special tiles (fill in f->special_tiles[]) for (u16 j = 0; j < CF_SPECIAL_COUNT; j++) { fillTileAttribs(tsrc, &f->special_tiles[j], &f->tiledef_special[j], tile_shader[j], use_normal_texture, f->tiledef_special[j].backface_culling, f->alpha, material_type); } if ((f->drawtype == NDT_MESH) && (f->mesh != "")) { // Meshnode drawtype // Read the mesh and apply scale f->mesh_ptr[0] = gamedef->getMesh(f->mesh); if (f->mesh_ptr[0]){ v3f scale = v3f(1.0, 1.0, 1.0) * BS * f->visual_scale; scaleMesh(f->mesh_ptr[0], scale); recalculateBoundingBox(f->mesh_ptr[0]); meshmanip->recalculateNormals(f->mesh_ptr[0], true, false); } } else if ((f->drawtype == NDT_NODEBOX) && ((f->node_box.type == NODEBOX_REGULAR) || (f->node_box.type == NODEBOX_FIXED)) && (!f->node_box.fixed.empty())) { //Convert regular nodebox nodes to meshnodes //Change the drawtype and apply scale f->drawtype = NDT_MESH; f->mesh_ptr[0] = convertNodeboxNodeToMesh(f); v3f scale = v3f(1.0, 1.0, 1.0) * f->visual_scale; scaleMesh(f->mesh_ptr[0], scale); recalculateBoundingBox(f->mesh_ptr[0]); meshmanip->recalculateNormals(f->mesh_ptr[0], true, false); } //Cache 6dfacedir and wallmounted rotated clones of meshes if (enable_mesh_cache && f->mesh_ptr[0] && (f->param_type_2 == CPT2_FACEDIR)) { for (u16 j = 1; j < 24; j++) { f->mesh_ptr[j] = cloneMesh(f->mesh_ptr[0]); rotateMeshBy6dFacedir(f->mesh_ptr[j], j); recalculateBoundingBox(f->mesh_ptr[j]); meshmanip->recalculateNormals(f->mesh_ptr[j], true, false); } } else if (enable_mesh_cache && f->mesh_ptr[0] && (f->param_type_2 == CPT2_WALLMOUNTED)) { static const u8 wm_to_6d[6] = {20, 0, 16+1, 12+3, 8, 4+2}; for (u16 j = 1; j < 6; j++) { f->mesh_ptr[j] = cloneMesh(f->mesh_ptr[0]); rotateMeshBy6dFacedir(f->mesh_ptr[j], wm_to_6d[j]); recalculateBoundingBox(f->mesh_ptr[j]); meshmanip->recalculateNormals(f->mesh_ptr[j], true, false); } rotateMeshBy6dFacedir(f->mesh_ptr[0], wm_to_6d[0]); recalculateBoundingBox(f->mesh_ptr[0]); meshmanip->recalculateNormals(f->mesh_ptr[0], true, false); } progress_callback(progress_callback_args, i, size); } #endif }
bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio) { bool enable_shaders = g_settings->getBool("enable_shaders"); bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping"); bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion"); if(!m_has_animation) { m_animation_force_timer = 100000; return false; } m_animation_force_timer = myrand_range(5, 100); // Cracks if(crack != m_last_crack) { for(std::map<u32, std::string>::iterator i = m_crack_materials.begin(); i != m_crack_materials.end(); i++) { scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first); std::string basename = i->second; // Create new texture name from original ITextureSource *tsrc = m_gamedef->getTextureSource(); std::ostringstream os; os<<basename<<crack; u32 new_texture_id = 0; video::ITexture *new_texture = tsrc->getTexture(os.str(), &new_texture_id); buf->getMaterial().setTexture(0, new_texture); // If the current material is also animated, // update animation info std::map<u32, TileSpec>::iterator anim_iter = m_animation_tiles.find(i->first); if(anim_iter != m_animation_tiles.end()){ TileSpec &tile = anim_iter->second; tile.texture = new_texture; tile.texture_id = new_texture_id; // force animation update m_animation_frames[i->first] = -1; } } m_last_crack = crack; } // Texture animation for(std::map<u32, TileSpec>::iterator i = m_animation_tiles.begin(); i != m_animation_tiles.end(); i++) { const TileSpec &tile = i->second; // Figure out current frame int frameoffset = m_animation_frame_offsets[i->first]; int frame = (int)(time * 1000 / tile.animation_frame_length_ms + frameoffset) % tile.animation_frame_count; // If frame doesn't change, skip if(frame == m_animation_frames[i->first]) continue; m_animation_frames[i->first] = frame; scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first); ITextureSource *tsrc = m_gamedef->getTextureSource(); // Create new texture name from original std::ostringstream os(std::ios::binary); os<<tsrc->getTextureName(tile.texture_id); os<<"^[verticalframe:"<<(int)tile.animation_frame_count<<":"<<frame; // Set the texture buf->getMaterial().setTexture(0, tsrc->getTexture(os.str())); buf->getMaterial().setTexture(2, tsrc->getTexture("disable_img.png")); if (enable_shaders && (enable_bumpmapping || enable_parallax_occlusion)) { std::string fname_base,fname_normal; fname_base = tsrc->getTextureName(tile.texture_id); unsigned pos; pos = fname_base.find("."); fname_normal = fname_base.substr (0, pos); fname_normal += "_normal.png"; if (tsrc->isKnownSourceImage(fname_normal)){ os.str(""); os<<fname_normal<<"^[verticalframe:"<<(int)tile.animation_frame_count<<":"<<frame; buf->getMaterial().setTexture(1, tsrc->getTexture(os.str())); buf->getMaterial().setTexture(2, tsrc->getTexture("enable_img.png")); } } } // Day-night transition if(daynight_ratio != m_last_daynight_ratio) { for(std::map<u32, std::map<u32, std::pair<u8, u8> > >::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<u32, std::pair<u8, u8 > >::iterator j = i->second.begin(); j != i->second.end(); j++) { u32 vertexIndex = j->first; u8 day = j->second.first; u8 night = j->second.second; finalColorBlend(vertices[vertexIndex].Color, day, night, daynight_ratio); // Brighten topside (no shaders) if(vertices[vertexIndex].Normal.Y > 0.5) { video::SColor &vc = vertices[vertexIndex].Color; vc.setRed (srgb_linear_multiply(vc.getRed(), 1.3, 255.0)); vc.setGreen(srgb_linear_multiply(vc.getGreen(), 1.3, 255.0)); vc.setBlue (srgb_linear_multiply(vc.getBlue(), 1.3, 255.0)); } } } m_last_daynight_ratio = daynight_ratio; } return true; }
bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio) { bool enable_shaders = g_settings->getBool("enable_shaders"); if(!m_has_animation) { m_animation_force_timer = 100000; return false; } m_animation_force_timer = myrand_range(5, 100); // Cracks if(crack != m_last_crack) { for(std::map<u32, std::string>::iterator i = m_crack_materials.begin(); i != m_crack_materials.end(); i++) { scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first); std::string basename = i->second; // Create new texture name from original ITextureSource *tsrc = m_gamedef->getTextureSource(); std::ostringstream os; os<<basename<<crack; u32 new_texture_id = 0; video::ITexture *new_texture = tsrc->getTexture(os.str(), &new_texture_id); buf->getMaterial().setTexture(0, new_texture); // If the current material is also animated, // update animation info std::map<u32, TileSpec>::iterator anim_iter = m_animation_tiles.find(i->first); if(anim_iter != m_animation_tiles.end()){ TileSpec &tile = anim_iter->second; tile.texture = new_texture; tile.texture_id = new_texture_id; // force animation update m_animation_frames[i->first] = -1; } } m_last_crack = crack; } // Texture animation for(std::map<u32, TileSpec>::iterator i = m_animation_tiles.begin(); i != m_animation_tiles.end(); i++) { const TileSpec &tile = i->second; // Figure out current frame int frameoffset = m_animation_frame_offsets[i->first]; int frame = (int)(time * 1000 / tile.animation_frame_length_ms + frameoffset) % tile.animation_frame_count; // If frame doesn't change, skip if(frame == m_animation_frames[i->first]) continue; m_animation_frames[i->first] = frame; scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first); ITextureSource *tsrc = m_gamedef->getTextureSource(); FrameSpec animation_frame = tile.frames.find(frame)->second; buf->getMaterial().setTexture(0, animation_frame.texture); if (enable_shaders) { if (animation_frame.normal_texture) { buf->getMaterial().setTexture(1, animation_frame.normal_texture); buf->getMaterial().setTexture(2, tsrc->getTexture("enable_img.png")); } else { buf->getMaterial().setTexture(2, tsrc->getTexture("disable_img.png")); } } } // Day-night transition if(daynight_ratio != m_last_daynight_ratio) { for(std::map<u32, std::map<u32, std::pair<u8, u8> > >::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<u32, std::pair<u8, u8 > >::iterator j = i->second.begin(); j != i->second.end(); j++) { u32 vertexIndex = j->first; u8 day = j->second.first; u8 night = j->second.second; finalColorBlend(vertices[vertexIndex].Color, day, night, daynight_ratio); } } m_last_daynight_ratio = daynight_ratio; } return true; }