diff --git a/data/textures/lava_flowing.png b/data/textures/lava_flowing.png new file mode 100644 index 0000000..432cc25 Binary files /dev/null and b/data/textures/lava_flowing.png differ diff --git a/data/textures/lava_source.png b/data/textures/lava_source.png new file mode 100644 index 0000000..305a233 Binary files /dev/null and b/data/textures/lava_source.png differ diff --git a/data/textures/water_flowing.png b/data/textures/water_flowing.png new file mode 100644 index 0000000..6f44c5c Binary files /dev/null and b/data/textures/water_flowing.png differ diff --git a/data/textures/water_source.png b/data/textures/water_source.png new file mode 100644 index 0000000..85fcf30 Binary files /dev/null and b/data/textures/water_source.png differ diff --git a/src/client.cpp b/src/client.cpp index 2e46565..ec538d7 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -235,7 +235,8 @@ Client::Client( m_time_of_day_update_timer(0), m_sleeping(false), m_waking(false), - m_sleep_state(0.0) + m_sleep_state(0.0), + m_animation_time(0.0) { m_mesh_update_thread.m_env = &m_env; m_packetcounter_timer = 0.0; @@ -314,6 +315,10 @@ void Client::step(float dtime) m_ignore_damage_timer = 0.0; } + m_animation_time += dtime; + if (m_animation_time > 60.0) + m_animation_time -= 60.0; + if (m_sleeping) { m_sleep_state += dtime; if (m_sleep_state > 1.0) diff --git a/src/client.h b/src/client.h index 5633eee..3ff5e02 100644 --- a/src/client.h +++ b/src/client.h @@ -341,6 +341,11 @@ public: u8 getSleepAlpha() {return m_sleep_state*255;} + float Client::getAnimationTime() + { + return m_animation_time; + } + private: // Virtual methods from con::PeerHandler @@ -416,6 +421,7 @@ private: bool m_sleeping; bool m_waking; float m_sleep_state; + float m_animation_time; }; #endif // !SERVER diff --git a/src/content_mapblock.cpp b/src/content_mapblock.cpp index 9db22c1..76a7c64 100644 --- a/src/content_mapblock.cpp +++ b/src/content_mapblock.cpp @@ -807,7 +807,7 @@ static void meshgen_cuboid( for (s32 j=0; j<4; j++) { vertices[i][j].Pos += pos; } - data->append(tiles[tileindex].getMaterial(), vertices[i], 4, indices, 6, colours); + data->append(tiles[tileindex], vertices[i], 4, indices, 6, colours); } } @@ -895,7 +895,7 @@ static void meshgen_rooftri(MeshMakeData *data, MapNode &n, v3s16 p, v3f corners tri_v[0].Pos += pos; tri_v[1].Pos += pos; tri_v[2].Pos += pos; - data->append(tile.getMaterial(),tri_v, 3, indices, 3, colours); + data->append(tile, tri_v, 3, indices, 3, colours); } { video::S3DVertex tri_v[3] = { @@ -913,7 +913,7 @@ static void meshgen_rooftri(MeshMakeData *data, MapNode &n, v3s16 p, v3f corners tri_v[0].Pos += pos; tri_v[1].Pos += pos; tri_v[2].Pos += pos; - data->append(tile.getMaterial(),tri_v, 3, indices, 3, colours); + data->append(tile, tri_v, 3, indices, 3, colours); } } @@ -949,7 +949,7 @@ void meshgen_cubelike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &sel vertices[i].Pos += pos; } - data->append(tile.getMaterial(), vertices, 4, indices, 6, colours); + data->append(tile, vertices, 4, indices, 6, colours); } if (meshgen_hardface(data,p,n,v3s16(1,0,0))) { TileSpec tile = getNodeTile(n,p,v3s16(1,0,0),selected,NULL); @@ -972,7 +972,7 @@ void meshgen_cubelike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &sel vertices[i].Pos += pos; } - data->append(tile.getMaterial(), vertices, 4, indices, 6, colours); + data->append(tile, vertices, 4, indices, 6, colours); } if (meshgen_hardface(data,p,n,v3s16(0,-1,0))) { TileSpec tile = getNodeTile(n,p,v3s16(0,-1,0),selected,NULL); @@ -995,7 +995,7 @@ void meshgen_cubelike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &sel vertices[i].Pos += pos; } - data->append(tile.getMaterial(), vertices, 4, indices, 6, colours); + data->append(tile, vertices, 4, indices, 6, colours); } if (meshgen_hardface(data,p,n,v3s16(0,1,0))) { TileSpec tile = getNodeTile(n,p,v3s16(0,1,0),selected,NULL); @@ -1018,7 +1018,7 @@ void meshgen_cubelike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &sel vertices[i].Pos += pos; } - data->append(tile.getMaterial(), vertices, 4, indices, 6, colours); + data->append(tile, vertices, 4, indices, 6, colours); } if (meshgen_hardface(data,p,n,v3s16(0,0,-1))) { TileSpec tile = getNodeTile(n,p,v3s16(0,0,-1),selected,NULL); @@ -1041,7 +1041,7 @@ void meshgen_cubelike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &sel vertices[i].Pos += pos; } - data->append(tile.getMaterial(), vertices, 4, indices, 6, colours); + data->append(tile, vertices, 4, indices, 6, colours); } if (meshgen_hardface(data,p,n,v3s16(0,0,1))) { TileSpec tile = getNodeTile(n,p,v3s16(0,0,1),selected,NULL); @@ -1064,7 +1064,7 @@ void meshgen_cubelike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &sel vertices[i].Pos += pos; } - data->append(tile.getMaterial(), vertices, 4, indices, 6, colours); + data->append(tile, vertices, 4, indices, 6, colours); } } @@ -1432,7 +1432,7 @@ void meshgen_dirtlike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &sel v[i].Pos += pos; } - data->append(toptile.getMaterial(), v, 4, indices, 6, colours); + data->append(toptile, v, 4, indices, 6, colours); if (!o_faces[2] || !o_faces[3] || !o_faces[4] || !o_faces[5]) { if (o_faces[2]) { @@ -1459,7 +1459,7 @@ void meshgen_dirtlike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &sel v2[i].TCoords += toptile.texture.pos; } - data->append(toptile.getMaterial(), v2, 4, indices, 6, colours); + data->append(toptile, v2, 4, indices, 6, colours); } if (o_faces[3]) { video::S3DVertex v2[4] = { @@ -1487,7 +1487,7 @@ void meshgen_dirtlike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &sel v2[i].TCoords += toptile.texture.pos; } - data->append(toptile.getMaterial(), v2, 4, indices, 6, colours); + data->append(toptile, v2, 4, indices, 6, colours); } if (o_faces[4]) { video::S3DVertex v2[4] = { @@ -1515,7 +1515,7 @@ void meshgen_dirtlike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &sel v2[i].TCoords += toptile.texture.pos; } - data->append(toptile.getMaterial(), v2, 4, indices, 6, colours); + data->append(toptile, v2, 4, indices, 6, colours); } if (o_faces[5]) { video::S3DVertex v2[4] = { @@ -1543,7 +1543,7 @@ void meshgen_dirtlike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &sel v2[i].TCoords += toptile.texture.pos; } - data->append(toptile.getMaterial(), v2, 4, indices, 6, colours); + data->append(toptile, v2, 4, indices, 6, colours); } } } @@ -1567,7 +1567,7 @@ void meshgen_dirtlike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &sel v[i].Pos += pos; } - data->append(basetile.getMaterial(), v, 4, indices, 6, colours); + data->append(basetile, v, 4, indices, 6, colours); } video::S3DVertex vertices[4] = { @@ -1654,7 +1654,7 @@ void meshgen_dirtlike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &sel v[i].Pos += pos; } - data->append(upstile.getMaterial(), v, 4, indices, 6, colours); + data->append(upstile, v, 4, indices, 6, colours); } if (!faces[face]) continue; @@ -1683,7 +1683,7 @@ void meshgen_dirtlike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &sel v[i].Pos += pos; } - data->append(basetile.getMaterial(), v, 4, indices, 6, colours); + data->append(basetile, v, 4, indices, 6, colours); if (!o_faces[face]) continue; @@ -1711,7 +1711,7 @@ void meshgen_dirtlike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &sel v[i].Pos += pos; } - data->append(sidetile.getMaterial(), v, 4, indices, 6, colours); + data->append(sidetile, v, 4, indices, 6, colours); } } @@ -2193,13 +2193,13 @@ void meshgen_plantlike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &se vertices[i].Pos.X += 0.005; } - data->append(tile.getMaterial(), vertices, 4, indices, 6, colours); + data->append(tile, vertices, 4, indices, 6, colours); for (u16 i=0; i<4; i++) { vertices[i].Pos.X -= 0.01; } - data->append(tile.getMaterial(), vertices, 4, indices, 6, colours); + data->append(tile, vertices, 4, indices, 6, colours); } }else{ for (u32 j=0; j<2; j++) { @@ -2232,7 +2232,7 @@ void meshgen_plantlike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &se vertices[i].Pos += pos; } - data->append(tile.getMaterial(), vertices, 4, indices, 6, colours); + data->append(tile, vertices, 4, indices, 6, colours); } } @@ -2259,7 +2259,7 @@ void meshgen_plantlike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &se vertices[i].Pos += pos; } - data->append(tile.getMaterial(), vertices, 4, indices, 6, colours); + data->append(tile, vertices, 4, indices, 6, colours); } } @@ -2406,7 +2406,7 @@ void meshgen_plantlike_fern(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNod v[i].Pos += pos; } - data->append(tile.getMaterial(), v, 4, indices, 6, colours); + data->append(tile, v, 4, indices, 6, colours); if (!selected.is_coloured && !selected.has_crack) continue; @@ -2415,7 +2415,7 @@ void meshgen_plantlike_fern(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNod v[i].Pos.X -= 0.01; } - data->append(tile.getMaterial(), v, 4, indices, 6, colours); + data->append(tile, v, 4, indices, 6, colours); } u32 max = (data->mesh_detail == 3 ? 8 : 4); for (u32 j=0; jappend(tile.getMaterial(), v, 8, indices, 12, colours); + data->append(tile, v, 8, indices, 12, colours); if (!selected.is_coloured && !selected.has_crack) continue; @@ -2450,7 +2450,7 @@ void meshgen_plantlike_fern(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNod v[i].Pos.Y -= 0.01; } - data->append(tile.getMaterial(), v, 8, indices, 12, colours); + data->append(tile, v, 8, indices, 12, colours); } if (selected.is_coloured || selected.has_crack) @@ -2476,7 +2476,7 @@ void meshgen_plantlike_fern(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNod vertices[i].Pos += pos; } - data->append(tile.getMaterial(), vertices, 4, indices, 6, colours); + data->append(tile, vertices, 4, indices, 6, colours); } } @@ -2700,7 +2700,7 @@ void meshgen_liquid(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &selec vertices[j].Pos += pos; } - data->append(tiles[i].getMaterial(), vertices, 4, indices, 6, colours); + data->append(tiles[i], vertices, 4, indices, 6, colours); } /* @@ -2735,7 +2735,7 @@ void meshgen_liquid(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &selec vertices[j].Pos += pos; } - data->append(tiles[0].getMaterial(), vertices, 4, indices, 6, colours); + data->append(tiles[0], vertices, 4, indices, 6, colours); } } @@ -2912,7 +2912,7 @@ void meshgen_liquid_source(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode for(u16 i=0; i<4; i++) { vertices[i].Pos += pos; } - data->append(tiles[j].getMaterial(), vertices, 4, indices, 6, colours); + data->append(tiles[j], vertices, 4, indices, 6, colours); } } @@ -3035,7 +3035,7 @@ void meshgen_glasslike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &se vertices[i].Pos += pos; } - data->append(tiles[j].getMaterial(), vertices, 4, indices, 6, colours); + data->append(tiles[j], vertices, 4, indices, 6, colours); } if (data->mesh_detail > 2 && data->light_detail > 2 && content_features(n.getContent()).light_source > 0) { @@ -3072,7 +3072,7 @@ void meshgen_glasslike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &se std::vector colours; meshgen_custom_lights(colours,255,255,255,255,4); - data->append(tile.getMaterial(), vertices, 4, indices, 6, colours); + data->append(tile, vertices, 4, indices, 6, colours); } } } @@ -3210,7 +3210,7 @@ void meshgen_torchlike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &se vertices[j][i].Pos += pos; } - data->append(tile.getMaterial(), vertices[j], 4, indices, 6, colours); + data->append(tile, vertices[j], 4, indices, 6, colours); } if (data->mesh_detail > 2 && data->light_detail > 2) { @@ -3246,7 +3246,7 @@ void meshgen_torchlike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &se std::vector colours; meshgen_custom_lights(colours,255,255,255,255,4); - data->append(tile.getMaterial(), vertices, 4, indices, 6, colours); + data->append(tile, vertices, 4, indices, 6, colours); } } } @@ -3477,7 +3477,7 @@ void meshgen_firelike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &sel vertices[i].Pos += pos; } - data->append(tile.getMaterial(), vertices, 4, indices, 6, colours); + data->append(tile, vertices, 4, indices, 6, colours); } } @@ -4653,7 +4653,7 @@ void meshgen_leaflike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &sel } } - data->append(tile.getMaterial(), vertices, 4, indices, 6, colours); + data->append(tile, vertices, 4, indices, 6, colours); } }else{ TileSpec tile = getNodeTile(n,p,v3s16(1,0,0),selected,NULL); @@ -4685,7 +4685,7 @@ void meshgen_leaflike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &sel vertices[i].Pos += pos; } - data->append(tile.getMaterial(), vertices, 4, indices, 6, colours); + data->append(tile, vertices, 4, indices, 6, colours); } } @@ -4869,7 +4869,7 @@ void meshgen_wirelike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &sel u16 indices[] = {0,1,2,2,3,0}; std::vector colours; meshgen_custom_lights(colours,cols[0],cols[1],cols[2],cols[3],4); - data->append(tiles[0].getMaterial(), vertices, 4, indices, 6, colours); + data->append(tiles[0], vertices, 4, indices, 6, colours); } { video::S3DVertex vertices[4] = { @@ -4884,7 +4884,7 @@ void meshgen_wirelike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &sel u16 indices[] = {0,1,2,2,3,0}; std::vector colours; meshgen_custom_lights(colours,cols[0],cols[1],cols[2],cols[3],4); - data->append(tiles[0].getMaterial(), vertices, 4, indices, 6, colours); + data->append(tiles[0], vertices, 4, indices, 6, colours); } }else{ if (x_plus) { @@ -4900,7 +4900,7 @@ void meshgen_wirelike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &sel u16 indices[] = {0,1,2,2,3,0}; std::vector colours; meshgen_custom_lights(colours,cols[0],cols[1],cols[2],cols[3],4); - data->append(tiles[0].getMaterial(), vertices, 4, indices, 6, colours); + data->append(tiles[0], vertices, 4, indices, 6, colours); } if (x_minus) { video::S3DVertex vertices[4] = { @@ -4915,7 +4915,7 @@ void meshgen_wirelike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &sel u16 indices[] = {0,1,2,2,3,0}; std::vector colours; meshgen_custom_lights(colours,cols[0],cols[1],cols[2],cols[3],4); - data->append(tiles[0].getMaterial(), vertices, 4, indices, 6, colours); + data->append(tiles[0], vertices, 4, indices, 6, colours); } if (z_plus) { video::S3DVertex vertices[4] = { @@ -4930,7 +4930,7 @@ void meshgen_wirelike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &sel u16 indices[] = {0,1,2,2,3,0}; std::vector colours; meshgen_custom_lights(colours,cols[0],cols[1],cols[2],cols[3],4); - data->append(tiles[0].getMaterial(), vertices, 4, indices, 6, colours); + data->append(tiles[0], vertices, 4, indices, 6, colours); } if (z_minus) { video::S3DVertex vertices[4] = { @@ -4945,7 +4945,7 @@ void meshgen_wirelike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &sel u16 indices[] = {0,1,2,2,3,0}; std::vector colours; meshgen_custom_lights(colours,cols[0],cols[1],cols[2],cols[3],4); - data->append(tiles[0].getMaterial(), vertices, 4, indices, 6, colours); + data->append(tiles[0], vertices, 4, indices, 6, colours); } if (x_plus_y) { video::S3DVertex vertices[4] = { @@ -4960,7 +4960,7 @@ void meshgen_wirelike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &sel u16 indices[] = {0,1,2,2,3,0}; std::vector colours; meshgen_custom_lights(colours,cols[0],cols[1],cols[2],cols[3],4); - data->append(tiles[0].getMaterial(), vertices, 4, indices, 6, colours); + data->append(tiles[0], vertices, 4, indices, 6, colours); } if (x_minus_y) { video::S3DVertex vertices[4] = { @@ -4975,7 +4975,7 @@ void meshgen_wirelike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &sel u16 indices[] = {0,1,2,2,3,0}; std::vector colours; meshgen_custom_lights(colours,cols[0],cols[1],cols[2],cols[3],4); - data->append(tiles[0].getMaterial(), vertices, 4, indices, 6, colours); + data->append(tiles[0], vertices, 4, indices, 6, colours); } if (z_plus_y) { video::S3DVertex vertices[4] = { @@ -4990,7 +4990,7 @@ void meshgen_wirelike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &sel u16 indices[] = {0,1,2,2,3,0}; std::vector colours; meshgen_custom_lights(colours,cols[0],cols[1],cols[2],cols[3],4); - data->append(tiles[0].getMaterial(), vertices, 4, indices, 6, colours); + data->append(tiles[0], vertices, 4, indices, 6, colours); } if (z_minus_y) { video::S3DVertex vertices[4] = { @@ -5005,7 +5005,7 @@ void meshgen_wirelike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &sel u16 indices[] = {0,1,2,2,3,0}; std::vector colours; meshgen_custom_lights(colours,cols[0],cols[1],cols[2],cols[3],4); - data->append(tiles[0].getMaterial(), vertices, 4, indices, 6, colours); + data->append(tiles[0], vertices, 4, indices, 6, colours); } } return; @@ -5319,7 +5319,7 @@ void meshgen_stairlike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &se vertices[i][j].Pos += pos; } - data->append(tiles[i].getMaterial(), vertices[i], vcounts[i], indices[i], icounts[i], colours); + data->append(tiles[i], vertices[i], vcounts[i], indices[i], icounts[i], colours); } }else{ for (int i=0; i<6; i++) { @@ -5343,7 +5343,7 @@ void meshgen_stairlike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &se vertices[i][j].Pos += pos; } - data->append(tiles[i].getMaterial(), vertices[i], vcounts[i], indices[i], icounts[i], colours); + data->append(tiles[i], vertices[i], vcounts[i], indices[i], icounts[i], colours); } } }else if (rot) { @@ -5368,7 +5368,7 @@ void meshgen_stairlike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &se vertices[i][j].Pos += pos; } - data->append(tiles[i].getMaterial(), vertices[i], vcounts[i], indices[i], icounts[i], colours); + data->append(tiles[i], vertices[i], vcounts[i], indices[i], icounts[i], colours); } }else{ for (int i=0; i<6; i++) { @@ -5389,7 +5389,7 @@ void meshgen_stairlike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &se vertices[i][j].Pos += pos; } - data->append(tiles[i].getMaterial(), vertices[i], vcounts[i], indices[i], icounts[i], colours); + data->append(tiles[i], vertices[i], vcounts[i], indices[i], icounts[i], colours); } } } @@ -5485,7 +5485,7 @@ void meshgen_slablike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &sel vertices[i][j].Pos += pos; } - data->append(tiles[i].getMaterial(), vertices[i], 4, indices, 6, colours); + data->append(tiles[i], vertices[i], 4, indices, 6, colours); } }else{ for (u16 i=0; i<6; i++) { @@ -5509,7 +5509,7 @@ void meshgen_slablike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &sel vertices[i][j].Pos += pos; } - data->append(tiles[i].getMaterial(), vertices[i], 4, indices, 6, colours); + data->append(tiles[i], vertices[i], 4, indices, 6, colours); } } } @@ -5673,7 +5673,7 @@ void meshgen_trunklike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &se v[k].Pos += pos; } - data->append(tile.getMaterial(), v, 10, indices, 24, colours); + data->append(tile, v, 10, indices, 24, colours); } if (!y_plus_any) { for (u16 j=0; j<4; j++) { @@ -5695,7 +5695,7 @@ void meshgen_trunklike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &se v[k].Pos += pos; } - data->append(endtile.getMaterial(), v, 6, end_indices, 12, colours); + data->append(endtile, v, 6, end_indices, 12, colours); } } if (!y_minus_any) { @@ -5719,7 +5719,7 @@ void meshgen_trunklike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &se v[k].Pos += pos; } - data->append(endtile.getMaterial(), v, 6, end_indices, 12, colours); + data->append(endtile, v, 6, end_indices, 12, colours); } }else if (mud_under) { for (u16 j=0; j<4; j++) { @@ -5741,7 +5741,7 @@ void meshgen_trunklike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &se v[k].Pos += pos; } - data->append(tile.getMaterial(), v, 6, base_indices, 12, colours); + data->append(tile, v, 6, base_indices, 12, colours); } } if (x_plus) { @@ -5777,7 +5777,7 @@ void meshgen_trunklike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &se v[k].Pos += pos; } - data->append(tile.getMaterial(), v, 10, indices, 24, colours); + data->append(tile, v, 10, indices, 24, colours); } } if (x_minus) { @@ -5812,7 +5812,7 @@ void meshgen_trunklike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &se v[k].Pos += pos; } - data->append(tile.getMaterial(), v, 10, indices, 24, colours); + data->append(tile, v, 10, indices, 24, colours); } } if (z_plus) { @@ -5847,7 +5847,7 @@ void meshgen_trunklike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &se v[k].Pos += pos; } - data->append(tile.getMaterial(), v, 10, indices, 24, colours); + data->append(tile, v, 10, indices, 24, colours); } } if (z_minus) { @@ -5883,7 +5883,7 @@ void meshgen_trunklike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &se v[k].Pos += pos; } - data->append(tile.getMaterial(), v, 10, indices, 24, colours); + data->append(tile, v, 10, indices, 24, colours); } } }else{ /* horizontal trunk */ @@ -5919,7 +5919,7 @@ void meshgen_trunklike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &se v[k].Pos += pos; } - data->append(tile.getMaterial(), v, 10, indices, 24, colours); + data->append(tile, v, 10, indices, 24, colours); } if (!x_plus_any) { for (u16 j=0; j<4; j++) { @@ -5942,7 +5942,7 @@ void meshgen_trunklike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &se v[k].Pos += pos; } - data->append(endtile.getMaterial(), v, 6, end_indices, 12, colours); + data->append(endtile, v, 6, end_indices, 12, colours); } } if (!x_minus_any) { @@ -5966,7 +5966,7 @@ void meshgen_trunklike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &se v[k].Pos += pos; } - data->append(endtile.getMaterial(), v, 6, end_indices, 12, colours); + data->append(endtile, v, 6, end_indices, 12, colours); } } if (z_plus) { @@ -6001,7 +6001,7 @@ void meshgen_trunklike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &se v[k].Pos += pos; } - data->append(tile.getMaterial(), v, 10, indices, 24, colours); + data->append(tile, v, 10, indices, 24, colours); } } if (z_minus) { @@ -6037,7 +6037,7 @@ void meshgen_trunklike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &se v[k].Pos += pos; } - data->append(tile.getMaterial(), v, 10, indices, 24, colours); + data->append(tile, v, 10, indices, 24, colours); } } }else{ // centred along Z @@ -6072,7 +6072,7 @@ void meshgen_trunklike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &se v[k].Pos += pos; } - data->append(tile.getMaterial(), v, 10, indices, 24, colours); + data->append(tile, v, 10, indices, 24, colours); } if (!z_plus_any) { for (u16 j=0; j<4; j++) { @@ -6095,7 +6095,7 @@ void meshgen_trunklike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &se v[k].Pos += pos; } - data->append(endtile.getMaterial(), v, 6, end_indices, 12, colours); + data->append(endtile, v, 6, end_indices, 12, colours); } } if (!z_minus_any) { @@ -6119,7 +6119,7 @@ void meshgen_trunklike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &se v[k].Pos += pos; } - data->append(endtile.getMaterial(), v, 6, end_indices, 12, colours); + data->append(endtile, v, 6, end_indices, 12, colours); } } if (x_plus) { @@ -6155,7 +6155,7 @@ void meshgen_trunklike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &se v[k].Pos += pos; } - data->append(tile.getMaterial(), v, 10, indices, 24, colours); + data->append(tile, v, 10, indices, 24, colours); } } if (x_minus) { @@ -6190,7 +6190,7 @@ void meshgen_trunklike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &se v[k].Pos += pos; } - data->append(tile.getMaterial(), v, 10, indices, 24, colours); + data->append(tile, v, 10, indices, 24, colours); } } } @@ -6269,7 +6269,7 @@ void meshgen_flaglike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &sel v[i].Pos += pos; } - data->append(flag.getMaterial(), v, 4, indices, 6, colours); + data->append(flag, v, 4, indices, 6, colours); } }else{ for (u16 i=0; i<4; i++) { @@ -6289,7 +6289,7 @@ void meshgen_flaglike(MeshMakeData *data, v3s16 p, MapNode &n, SelectedNode &sel vertices[i].Pos += pos; } - data->append(flag.getMaterial(), vertices, 4, indices, 6, colours); + data->append(flag, vertices, 4, indices, 6, colours); } delete meta; } @@ -6343,7 +6343,7 @@ void meshgen_farnode(MeshMakeData *data, v3s16 p, MapNode &n) vertices[i].Pos += pos; } - data->appendFar(tile.getMaterial(), vertices, 4, indices, 6); + data->appendFar(tile, vertices, 4, indices, 6); } if (meshgen_farface(data,p,n,v3s16(1,0,0))) { TileSpec tile = getNodeTile(n,p,v3s16(1,0,0),selected,NULL); @@ -6360,7 +6360,7 @@ void meshgen_farnode(MeshMakeData *data, v3s16 p, MapNode &n) vertices[i].Pos += pos; } - data->appendFar(tile.getMaterial(), vertices, 4, indices, 6); + data->appendFar(tile, vertices, 4, indices, 6); } if (meshgen_farface(data,p,n,v3s16(0,-1,0))) { TileSpec tile = getNodeTile(n,p,v3s16(0,-1,0),selected,NULL); @@ -6378,7 +6378,7 @@ void meshgen_farnode(MeshMakeData *data, v3s16 p, MapNode &n) vertices[i].Pos += pos; } - data->appendFar(tile.getMaterial(), vertices, 4, indices, 6); + data->appendFar(tile, vertices, 4, indices, 6); } if (meshgen_farface(data,p,n,v3s16(0,1,0))) { TileSpec tile = getNodeTile(n,p,v3s16(0,1,0),selected,NULL); @@ -6395,7 +6395,7 @@ void meshgen_farnode(MeshMakeData *data, v3s16 p, MapNode &n) vertices[i].Pos += pos; } - data->appendFar(tile.getMaterial(), vertices, 4, indices, 6); + data->appendFar(tile, vertices, 4, indices, 6); } if (meshgen_farface(data,p,n,v3s16(0,0,-1))) { TileSpec tile = getNodeTile(n,p,v3s16(0,0,-1),selected,NULL); @@ -6412,7 +6412,7 @@ void meshgen_farnode(MeshMakeData *data, v3s16 p, MapNode &n) vertices[i].Pos += pos; } - data->appendFar(tile.getMaterial(), vertices, 4, indices, 6); + data->appendFar(tile, vertices, 4, indices, 6); } if (meshgen_farface(data,p,n,v3s16(0,0,1))) { TileSpec tile = getNodeTile(n,p,v3s16(0,0,1),selected,NULL); @@ -6429,9 +6429,8 @@ void meshgen_farnode(MeshMakeData *data, v3s16 p, MapNode &n) vertices[i].Pos += pos; } - data->appendFar(tile.getMaterial(), vertices, 4, indices, 6); + data->appendFar(tile, vertices, 4, indices, 6); } } #endif - diff --git a/src/content_mapnode.cpp b/src/content_mapnode.cpp index f332a19..a5dae1c 100644 --- a/src/content_mapnode.cpp +++ b/src/content_mapnode.cpp @@ -2142,7 +2142,9 @@ void content_mapnode_init(bool repeat) i = CONTENT_WATER; f = &content_features(i); f->description = wgettext("Water"); - f->setAllTextures("water.png"); + f->animation_length = 0.8; + f->setAllTextureFlags(MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES); + f->setAllTextures("water_flowing.png"); f->setInventoryTextureCube("water.png", "water.png", "water.png"); f->param_type = CPT_LIGHT; f->param2_type = CPT_LIQUID; @@ -2160,7 +2162,6 @@ void content_mapnode_init(bool repeat) #ifndef SERVER if(!opaque_water) f->vertex_alpha = WATER_ALPHA; - f->setAllTextureFlags(0); f->setAllTextureTypes(MATERIAL_ALPHA_VERTEX); f->post_effect_color = video::SColor(64, 100, 100, 200); #endif @@ -2170,7 +2171,9 @@ void content_mapnode_init(bool repeat) i = CONTENT_WATERSOURCE; f = &content_features(i); f->description = wgettext("Water"); - f->setAllTextures("water.png"); + f->animation_length = 2.0; + f->setAllTextureFlags(MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES); + f->setAllTextures("water_source.png"); f->setInventoryTextureCube("water.png", "water.png", "water.png"); f->param_type = CPT_LIGHT; f->param2_type = CPT_LIQUID; @@ -2187,7 +2190,6 @@ void content_mapnode_init(bool repeat) #ifndef SERVER if(!opaque_water) f->vertex_alpha = WATER_ALPHA; - f->setAllTextureFlags(0); f->setAllTextureTypes(MATERIAL_ALPHA_VERTEX); f->post_effect_color = video::SColor(64, 100, 100, 200); #endif @@ -2199,7 +2201,9 @@ void content_mapnode_init(bool repeat) i = CONTENT_LAVA; f = &content_features(i); f->description = wgettext("Lava"); - f->setAllTextures("lava.png"); + f->animation_length = 3.0; + f->setAllTextureFlags(MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES); + f->setAllTextures("lava_flowing.png"); f->setInventoryTextureCube("lava.png", "lava.png", "lava.png"); f->param_type = CPT_LIGHT; f->param2_type = CPT_LIQUID; @@ -2225,7 +2229,9 @@ void content_mapnode_init(bool repeat) i = CONTENT_LAVASOURCE; f = &content_features(i); f->description = wgettext("Lava"); - f->setAllTextures("lava.png"); + f->animation_length = 3.0; + f->setAllTextureFlags(MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES); + f->setAllTextures("lava_source.png"); f->setInventoryTextureCube("lava.png", "lava.png", "lava.png"); f->param_type = CPT_LIGHT; f->param2_type = CPT_LIQUID; diff --git a/src/map.cpp b/src/map.cpp index 2279340..3a452ec 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -3090,6 +3090,8 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) // Blocks from which stuff was actually drawn u32 blocks_without_stuff = 0; + float anim_time = m_client->getAnimationTime(); + /* Collect a set of blocks for drawing */ @@ -3272,6 +3274,12 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) ) continue; + // Animate textures in block mesh + if (block->mesh->isAnimated()) { + //JMutexAutoLock lock(block->mesh_mutex); //needed? + block->mesh->animate(anim_time); + } + // Add to set drawset[block->getPos()] = block; diff --git a/src/mapblock_mesh.cpp b/src/mapblock_mesh.cpp index 4394907..5abd9c0 100644 --- a/src/mapblock_mesh.cpp +++ b/src/mapblock_mesh.cpp @@ -466,6 +466,49 @@ MapBlockMesh::~MapBlockMesh() m_mesh = NULL; m_farmesh->drop(); m_farmesh = NULL; + if (!m_animation_data.empty()) + m_animation_data.clear(); +} + +void MapBlockMesh::animate(float time) +{ + if (!m_mesh) + return; + + for (std::map::iterator it = m_animation_data.begin(); + it != m_animation_data.end(); ++it) { + + AnimationData temp_data = it->second; + const TileSpec &tile = temp_data.tile; + // Figure out current frame + int frame = (int)(time * 1000 / tile.animation_frame_length_ms) % tile.animation_frame_count; + + // If frame doesn't change, skip + if (frame == temp_data.frame) + continue; + + temp_data.frame = frame; + + m_animation_data[it->first] = temp_data; + + // Make sure we don't cause an overflow. Can get removed if future is no problems occuring + if (it->first >= m_mesh->getMeshBufferCount()) { + errorstream << ": animate() Tying to index non existent Buffer." << std::endl; + return; + } + + scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(it->first); + + // Create new texture name from original + if (g_texturesource && buf != 0) { + std::ostringstream os(std::ios::binary); + os << g_texturesource->getTextureName(tile.texture.id); + os << "^[verticalframe:" << (int)tile.animation_frame_count << ":" << frame; + // Set the texture + AtlasPointer ap = g_texturesource->getTexture(os.str()); + buf->getMaterial().setTexture(0, ap.atlas); + } + } } void MapBlockMesh::generate(MeshMakeData *data, v3s16 camera_offset, JMutex *mutex) @@ -479,6 +522,8 @@ void MapBlockMesh::generate(MeshMakeData *data, v3s16 camera_offset, JMutex *mut data->light_detail = g_settings->getU16("light_detail"); m_pos = data->m_blockpos; SelectedNode selected; + if (!m_animation_data.empty()) + m_animation_data.clear(); for(s16 z=0; zm_meshdata.size(); i++) { MeshData &d = data->m_meshdata[i]; + + // - Texture animation + if (d.tile.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES) { + // Add to MapBlockMesh in order to animate these tiles + AnimationData anim_data; + anim_data.tile = d.tile; + anim_data.frame = 0; + m_animation_data[i] = anim_data; + } + // Create meshbuffer // This is a "Standard MeshBuffer", // it's a typedeffed CMeshBuffer scene::SMeshBuffer *buf = new scene::SMeshBuffer(); // Set material - buf->Material = d.material; + buf->Material = d.tile.getMaterial(); // Add to mesh mesh->addMeshBuffer(buf); // Mesh grabbed it @@ -646,7 +701,7 @@ void MapBlockMesh::generate(MeshMakeData *data, v3s16 camera_offset, JMutex *mut // it's a typedeffed CMeshBuffer scene::SMeshBuffer *buf = new scene::SMeshBuffer(); // Set material - buf->Material = d.material; + buf->Material = d.tile.getMaterial(); // Add to mesh fmesh->addMeshBuffer(buf); // Mesh grabbed it @@ -670,6 +725,7 @@ void MapBlockMesh::generate(MeshMakeData *data, v3s16 camera_offset, JMutex *mut m_meshdata.swap(data->m_meshdata); m_fardata.swap(data->m_fardata); refresh(data->m_daynight_ratio); + animate(0.0);// get first frame of animation m_mesh->recalculateBoundingBox(); if (mutex != NULL) diff --git a/src/mapblock_mesh.h b/src/mapblock_mesh.h index ccbe79d..f0a19d3 100644 --- a/src/mapblock_mesh.h +++ b/src/mapblock_mesh.h @@ -50,12 +50,12 @@ struct MeshData { bool single; v3s16 pos; - video::SMaterial material; + TileSpec tile; std::vector indices; std::vector vertices; std::vector colours; std::vector siblings; - MeshData* parent; + MeshData *parent; }; struct MapBlockSound @@ -98,12 +98,12 @@ struct MeshMakeData */ void fill(u32 daynight_ratio, MapBlock *block); - void startSingle(v3s16 pos, video::SMaterial material) + void startSingle(v3s16 pos, TileSpec tile) { MeshData dd; dd.single = true; dd.pos = pos; - dd.material = material; + dd.tile = tile; dd.parent = NULL; m_meshdata.push_back(dd); m_single = &m_meshdata[m_meshdata.size()-1]; @@ -113,7 +113,7 @@ struct MeshMakeData m_single = NULL; } void append( - video::SMaterial material, + TileSpec tile, const video::S3DVertex* const vertices, u32 v_count, const u16* const indices, @@ -125,9 +125,9 @@ struct MeshMakeData if (m_single) { if (m_single->parent) m_single = m_single->parent; - if (m_single->material != material) { + if (m_single->tile != tile) { for (u16 i=0; isiblings.size(); i++) { - if (m_single->siblings[i]->material == material) { + if (m_single->siblings[i]->tile == tile) { d = m_single->siblings[i]; break; } @@ -136,7 +136,7 @@ struct MeshMakeData MeshData dd; dd.single = true; dd.pos = m_single->pos; - dd.material = material; + dd.tile = tile; dd.parent = m_single; m_meshdata.push_back(dd); d = &m_meshdata[m_meshdata.size()-1]; @@ -148,7 +148,7 @@ struct MeshMakeData }else{ for (u32 i=0; i 65535) continue; @@ -160,7 +160,7 @@ struct MeshMakeData if (d == NULL) { MeshData dd; dd.single = false; - dd.material = material; + dd.tile = tile; m_meshdata.push_back(dd); d = &m_meshdata[m_meshdata.size()-1]; } @@ -180,7 +180,7 @@ struct MeshMakeData } } void appendFar( - video::SMaterial material, + TileSpec tile, const video::S3DVertex* const vertices, u32 v_count, const u16* const indices, @@ -190,7 +190,7 @@ struct MeshMakeData MeshData *d = NULL; for (u32 i=0; i 65535) continue; @@ -202,7 +202,7 @@ struct MeshMakeData if (d == NULL) { MeshData dd; dd.single = false; - dd.material = material; + dd.tile = tile; m_fardata.push_back(dd); d = &m_fardata[m_fardata.size()-1]; } @@ -218,6 +218,12 @@ struct MeshMakeData } }; +struct AnimationData +{ + TileSpec tile; + int frame; +}; + class MapBlockMesh { public: @@ -236,6 +242,12 @@ public: void generate(MeshMakeData *data, v3s16 camera_offset, JMutex *mutex); void refresh(u32 daynight_ratio); + void animate(float time); + + bool isAnimated() + { + return !m_animation_data.empty(); + } void updateCameraOffset(v3s16 camera_offset); @@ -247,7 +259,8 @@ private: v3s16 m_camera_offset; std::vector m_meshdata; std::vector m_fardata; + + std::map m_animation_data; }; #endif - diff --git a/src/mapnode.cpp b/src/mapnode.cpp index 642b58a..72fae43 100644 --- a/src/mapnode.cpp +++ b/src/mapnode.cpp @@ -102,9 +102,28 @@ void ContentFeatures::setTexture(u16 i, std::string name, u8 alpha) { used_texturenames[name] = true; - if(g_texturesource) - { + if (g_texturesource) { tiles[i].texture = g_texturesource->getTexture(name); + + // we have an animated texture! + if (tiles[i].material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES) { + // Get raw texture size to determine frame count by aspect ratio + video::ITexture *t = g_texturesource->getTextureRaw(name); + if (t != NULL) { + v2u32 size = t->getOriginalSize(); + int frame_count = size.Y / size.X; + int frame_length_ms = 1000.0 * animation_length / frame_count; + tiles[i].animation_frame_count = frame_count; + tiles[i].animation_frame_length_ms = frame_length_ms; + + // If there are no frames for an animation, switch + // animation off (so that having specified an animation + // for something but not using it in the texture pack + // gives no overhead) + if (frame_count == 1) + tiles[i].material_flags &= ~MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES; + } + } } if(alpha != 255) @@ -721,5 +740,3 @@ u8 face_light(MapNode n, MapNode n2, v3s16 face_dir) return (ln<<4)|ld; } - - diff --git a/src/mapnode.h b/src/mapnode.h index 1378546..20d171d 100644 --- a/src/mapnode.h +++ b/src/mapnode.h @@ -300,6 +300,8 @@ struct ContentFeatures TileSpec tiles[6]; TileSpec meta_tiles[6]; + float animation_length; + video::ITexture *inventory_texture; std::string inventory_texture_name; @@ -528,6 +530,7 @@ struct ContentFeatures param_type = CPT_NONE; param2_type = CPT_NONE; draw_type = CDT_AIRLIKE; + animation_length = 1.0; is_ground_content = false; light_propagates = false; sunlight_propagates = false; diff --git a/src/tile.cpp b/src/tile.cpp index 58c12b9..1312ee7 100644 --- a/src/tile.cpp +++ b/src/tile.cpp @@ -647,7 +647,7 @@ void TextureSource::buildMainAtlas() src_x = pos_in_atlas.X; } s32 y = y0 + pos_in_atlas.Y; - s32 src_y = MYMAX(pos_in_atlas.Y, MYMIN(pos_in_atlas.Y + (s32)dim.Height - 1, y)); + s32 src_y = MYMAX((int)pos_in_atlas.Y, MYMIN((int)pos_in_atlas.Y + (int)dim.Height - 1, y)); s32 dst_y = y; video::SColor c = atlas_img->getPixel(src_x, src_y); atlas_img->setPixel(dst_x,dst_y,c); @@ -1255,8 +1255,7 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg, video::IImage *image = driver->createImageFromFile(path.c_str()); - if(image == NULL) - { + if (image == NULL) { infostream<<"generate_image(): Loading path \"" <drop(); } + /* + [verticalframe:N:I + Crops a frame of a vertical animation. + N = frame count, I = frame index + */ + else if(part_of_name.substr(0,15) == "[verticalframe:") + { + Strfnd sf(part_of_name); + sf.next(":"); + u32 frame_count = stoi(sf.next(":")); + u32 frame_index = stoi(sf.next(":")); + + if(baseimg == NULL){ + errorstream<<"generate_image(): baseimg!=NULL " + <<"for part_of_name=\""<getDimension(); + frame_size.Y /= frame_count; + + video::IImage *img = driver->createImage(video::ECF_A8R8G8B8, + frame_size); + if (!img) { + errorstream <<"generate_image(): Could not create image " + << "for part_of_name=\"" << part_of_name + << "\", cancelling." << std::endl; + return false; + } + + // Fill target image with transparency + img->fill(video::SColor(0,0,0,0)); + + core::dimension2d dim = frame_size; + core::position2d pos_dst(0, 0); + core::position2d pos_src(0, frame_index * frame_size.Y); + baseimg->copyToWithAlpha(img, pos_dst, + core::rect(pos_src, dim), + video::SColor(255,255,255,255), + NULL); + // Replace baseimg + baseimg->drop(); + baseimg = img; + } /* [text:x,y,X,Y,string writes string to texture diff --git a/src/tile.h b/src/tile.h index 1c97d43..064609d 100644 --- a/src/tile.h +++ b/src/tile.h @@ -271,6 +271,7 @@ enum MaterialType{ // Material flags #define MATERIAL_FLAG_BACKFACE_CULLING 0x01 +#define MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES 0x02 /* This fully defines the looks of a tile. @@ -287,8 +288,12 @@ struct TileSpec material_flags( //0 // <- DEBUG, Use the one below MATERIAL_FLAG_BACKFACE_CULLING - ) + ), + animation_frame_count(0), + animation_frame_length_ms(0) { + video::SMaterial m; + m_material = m; } bool operator==(TileSpec &other) @@ -301,6 +306,11 @@ struct TileSpec ); } + bool operator!=(TileSpec &other) + { + return !(*this == other); + } + // Sets everything else except the texture in the material void applyMaterialOptions(video::SMaterial &material) const { @@ -327,10 +337,9 @@ struct TileSpec video::SMaterial getMaterial() { - video::SMaterial m; - applyMaterialOptions(m); - m.setTexture(0,texture.atlas); - return m; + applyMaterialOptions(m_material); + m_material.setTexture(0, texture.atlas); + return m_material; } AtlasPointer texture; @@ -340,6 +349,11 @@ struct TileSpec u8 material_type; // Material flags u8 material_flags; + + u8 animation_frame_count; + u16 animation_frame_length_ms; + + video::SMaterial m_material; }; #endif