Esempio n. 1
0
void Hud::drawLuaElements(v3s16 camera_offset) {
	for (size_t i = 0; i != player->hud.size(); i++) {
		HudElement *e = player->hud[i];
		if (!e)
			continue;
		
		v2s32 pos(floor(e->pos.X * (float) m_screensize.X + 0.5),
				floor(e->pos.Y * (float) m_screensize.Y + 0.5));
		switch (e->type) {
			case HUD_ELEM_IMAGE: {
				video::ITexture *texture = tsrc->getTexture(e->text);
				if (!texture)
					continue;

				const video::SColor color(255, 255, 255, 255);
				const video::SColor colors[] = {color, color, color, color};
				core::dimension2di imgsize(texture->getOriginalSize());
				v2s32 dstsize(imgsize.Width * e->scale.X,
				              imgsize.Height * e->scale.Y);
				if (e->scale.X < 0)
					dstsize.X = m_screensize.X * (e->scale.X * -0.01);
				if (e->scale.Y < 0)
					dstsize.Y = m_screensize.Y * (e->scale.Y * -0.01);
				v2s32 offset((e->align.X - 1.0) * dstsize.X / 2,
				             (e->align.Y - 1.0) * dstsize.Y / 2);
				core::rect<s32> rect(0, 0, dstsize.X, dstsize.Y);
				rect += pos + offset + v2s32(e->offset.X, e->offset.Y);
				driver->draw2DImage(texture, rect,
					core::rect<s32>(core::position2d<s32>(0,0), imgsize),
					NULL, colors, true);
				break; }
			case HUD_ELEM_TEXT: {
				video::SColor color(255, (e->number >> 16) & 0xFF,
										 (e->number >> 8)  & 0xFF,
										 (e->number >> 0)  & 0xFF);
				core::rect<s32> size(0, 0, e->scale.X, text_height * e->scale.Y);
				std::wstring text = narrow_to_wide(e->text);
				core::dimension2d<u32> textsize = font->getDimension(text.c_str());
				v2s32 offset((e->align.X - 1.0) * (textsize.Width / 2),
				             (e->align.Y - 1.0) * (textsize.Height / 2));
				v2s32 offs(e->offset.X, e->offset.Y);
				font->draw(text.c_str(), size + pos + offset + offs, color);
				break; }
			case HUD_ELEM_STATBAR: {
				v2s32 offs(e->offset.X, e->offset.Y);
				drawStatbar(pos, HUD_CORNER_UPPER, e->dir, e->text, e->number, offs, e->size);
				break; }
			case HUD_ELEM_INVENTORY: {
				InventoryList *inv = inventory->getList(e->text);
				drawItems(pos, e->number, 0, inv, e->item, e->dir);
				break; }
			case HUD_ELEM_WAYPOINT: {
				v3f p_pos = player->getPosition() / BS;
				v3f w_pos = e->world_pos * BS;
				float distance = floor(10 * p_pos.getDistanceFrom(e->world_pos)) / 10;
				scene::ICameraSceneNode* camera = smgr->getActiveCamera();
				w_pos -= intToFloat(camera_offset, BS);
				core::matrix4 trans = camera->getProjectionMatrix();
				trans *= camera->getViewMatrix();
				f32 transformed_pos[4] = { w_pos.X, w_pos.Y, w_pos.Z, 1.0f };
				trans.multiplyWith1x4Matrix(transformed_pos);
				if (transformed_pos[3] < 0)
					break;
				f32 zDiv = transformed_pos[3] == 0.0f ? 1.0f :
					core::reciprocal(transformed_pos[3]);
				pos.X = m_screensize.X * (0.5 * transformed_pos[0] * zDiv + 0.5);
				pos.Y = m_screensize.Y * (0.5 - transformed_pos[1] * zDiv * 0.5);
				video::SColor color(255, (e->number >> 16) & 0xFF,
										 (e->number >> 8)  & 0xFF,
										 (e->number >> 0)  & 0xFF);
				core::rect<s32> size(0, 0, 200, 2 * text_height);
				std::wstring text = narrow_to_wide(e->name);
				font->draw(text.c_str(), size + pos, color);
				std::ostringstream os;
				os<<distance<<e->text;
				text = narrow_to_wide(os.str());
				pos.Y += text_height;
				font->draw(text.c_str(), size + pos, color);
				break; }
			default:
				infostream << "Hud::drawLuaElements: ignoring drawform " << e->type <<
					" of hud element ID " << i << " due to unrecognized type" << std::endl;
		}
	}
}
Esempio n. 2
0
void Camera::update(LocalPlayer* player, f32 frametime, v2u32 screensize)
{
	// Set player node transformation
	m_playernode->setPosition(player->getPosition());
	m_playernode->setRotation(v3f(0, -1 * player->getYaw(), 0));
	m_playernode->updateAbsolutePosition();

	// Set head node transformation
	m_headnode->setPosition(player->getEyeOffset());
	m_headnode->setRotation(v3f(player->getPitch(), 0, 0));
	m_headnode->updateAbsolutePosition();

	// Compute relative camera position and target
	v3f rel_cam_pos = v3f(0,0,0);
	v3f rel_cam_target = v3f(0,0,1);
	v3f rel_cam_up = v3f(0,1,0);

	if (m_view_bobbing_state != 0 && m_view_bobbing_anim != 0) {
		f32 bobfrac = my_modf(m_view_bobbing_anim * 2);
		f32 bobdir = (m_view_bobbing_anim < 0.5) ? 1.0 : -1.0;
		f32 bobknob = 1.2;
		f32 bobtmp = sin(pow(bobfrac, bobknob) * PI);

		v3f bobvec = v3f(
			0.3 * bobdir * sin(bobfrac * PI),
			-0.28 * bobtmp * bobtmp,
			0.);

		float f = 1.0;
		f *= m_view_bobbing_amount;
		rel_cam_pos += bobvec * f;
		rel_cam_target += bobvec * f;
		rel_cam_target.Z -= 0.005 * bobvec.Z * f;
		rel_cam_up.rotateXYBy(-0.03 * bobdir * bobtmp * PI * f);

	}

	// Compute absolute camera position and target
	m_headnode->getAbsoluteTransformation().transformVect(m_camera_position, rel_cam_pos);
	m_headnode->getAbsoluteTransformation().rotateVect(m_camera_direction, rel_cam_target - rel_cam_pos);

	v3f abs_cam_up;
	m_headnode->getAbsoluteTransformation().rotateVect(abs_cam_up, rel_cam_up);

	// Update offset if too far away from the center of the map

	m_camera_offset.X += CAMERA_OFFSET_STEP*
			(((s16)(m_camera_position.X/BS) - m_camera_offset.X)/CAMERA_OFFSET_STEP);
	m_camera_offset.Y += CAMERA_OFFSET_STEP*
			(((s16)(m_camera_position.Y/BS) - m_camera_offset.Y)/CAMERA_OFFSET_STEP);
	m_camera_offset.Z += CAMERA_OFFSET_STEP*
			(((s16)(m_camera_position.Z/BS) - m_camera_offset.Z)/CAMERA_OFFSET_STEP);

	// Set camera node transformation
	m_cameranode->setPosition(m_camera_position-intToFloat(m_camera_offset, BS));
	m_cameranode->setUpVector(abs_cam_up);
	// *100.0 helps in large map coordinates
	m_cameranode->setTarget(m_camera_position-intToFloat(m_camera_offset, BS) + 100 * m_camera_direction);

	// FOV and aspect ratio
	m_aspect = (f32)screensize.X / (f32) screensize.Y;
	m_fov_y = m_fov * PI / 180.0;
	m_fov_x = 2 * atan(0.5 * m_aspect * tan(m_fov_y));
	m_cameranode->setAspectRatio(m_aspect);
	m_cameranode->setFOV(m_fov_y);

	// Position the wielded item
	v3f wield_position = m_wieldnode_baseposition;
	v3f wield_rotation = m_wieldnode_baserotation;
	if (m_digging_button != -1) {
		f32 digfrac = m_digging_anim;
		wield_position.X -= 30 * sin(pow(digfrac, 0.8f) * PI);
		wield_position.Y += 15 * sin(digfrac * 2 * PI);
		wield_position.Z += 5 * digfrac;

		// Euler angles are PURE EVIL, so why not use quaternions?
		core::quaternion quat_begin(wield_rotation * core::DEGTORAD);
		core::quaternion quat_end(v3f(90, -10, -130) * core::DEGTORAD);
		core::quaternion quat_slerp;
		quat_slerp.slerp(quat_begin, quat_end, sin(digfrac * PI));
		quat_slerp.toEuler(wield_rotation);
		wield_rotation *= core::RADTODEG;
	}else{
		f32 bobfrac = my_modf(m_view_bobbing_anim);
		wield_position.X -= sin(bobfrac*PI*2.0) * 3.0;
		wield_position.Y += sin(my_modf(bobfrac*2.0)*PI) * 3.0;
	}
	m_wieldnode->setPosition(wield_position);
	m_wieldnode->setRotation(wield_rotation);

	u8 li = decode_light(player->light);
	// Set brightness one lower than incoming light
	diminish_light(li);
	m_wieldnode->updateLight(li);

	// Render distance feedback loop
	updateViewingRange(frametime);

	// If the player seems to be walking on solid ground,
	// view bobbing is enabled and free_move is off,
	// start (or continue) the view bobbing animation.
	v3f speed = player->getSpeed();
	if (
		(hypot(speed.X, speed.Z) > BS)
		&& (player->touching_ground)
		&& m_view_bobbing == true
		&& player->control.free == false
	) {
		// Start animation
		m_view_bobbing_state = 1;
		m_view_bobbing_speed = MYMIN(speed.getLength(), 60)*1.2;
	}else if (m_view_bobbing_state == 1) {
		// Stop animation
		m_view_bobbing_state = 2;
		m_view_bobbing_speed = 60;
	}
}
Esempio n. 3
0
void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
		core::list<CollisionInfo> *collision_info)
{
	INodeDefManager *nodemgr = m_gamedef->ndef();

	v3f position = getPosition();
	v3f oldpos = position;
	v3s16 oldpos_i = floatToInt(oldpos, BS);

	v3f old_speed = m_speed;

	/*std::cout<<"oldpos_i=("<<oldpos_i.X<<","<<oldpos_i.Y<<","
			<<oldpos_i.Z<<")"<<std::endl;*/

	/*
		Calculate new position
	*/
	position += m_speed * dtime;
	
	// Skip collision detection if a special movement mode is used
	bool free_move = g_settings->getBool("free_move");
	if(free_move)
	{
		setPosition(position);
		return;
	}

	/*
		Collision detection
	*/
	
	// Player position in nodes
	v3s16 pos_i = floatToInt(position, BS);
	
	/*
		Check if player is in water (the oscillating value)
	*/
	try{
		// If in water, the threshold of coming out is at higher y
		if(in_water)
		{
			v3s16 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
			in_water = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
		}
		// If not in water, the threshold of going in is at lower y
		else
		{
			v3s16 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
			in_water = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
		}
	}
	catch(InvalidPositionException &e)
	{
		in_water = false;
	}

	/*
		Check if player is in water (the stable value)
	*/
	try{
		v3s16 pp = floatToInt(position + v3f(0,0,0), BS);
		in_water_stable = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
	}
	catch(InvalidPositionException &e)
	{
		in_water_stable = false;
	}

	/*
	        Check if player is climbing
	*/

	try {
		v3s16 pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
		v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
		is_climbing = ((nodemgr->get(map.getNode(pp).getContent()).climbable ||
		nodemgr->get(map.getNode(pp2).getContent()).climbable) && !free_move);
	}
	catch(InvalidPositionException &e)
	{
		is_climbing = false;
	}

	/*
		Collision uncertainty radius
		Make it a bit larger than the maximum distance of movement
	*/
	//f32 d = pos_max_d * 1.1;
	// A fairly large value in here makes moving smoother
	f32 d = 0.15*BS;

	// This should always apply, otherwise there are glitches
	assert(d > pos_max_d);

	float player_radius = BS*0.35;
	float player_height = BS*1.7;
	
	// Maximum distance over border for sneaking
	f32 sneak_max = BS*0.4;

	/*
		If sneaking, player has larger collision radius to keep from
		falling
	*/
	/*if(control.sneak)
		player_radius = sneak_max + d*1.1;*/
	
	/*
		If sneaking, keep in range from the last walked node and don't
		fall off from it
	*/
	if(control.sneak && m_sneak_node_exists)
	{
		f32 maxd = 0.5*BS + sneak_max;
		v3f lwn_f = intToFloat(m_sneak_node, BS);
		position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd);
		position.Z = rangelim(position.Z, lwn_f.Z-maxd, lwn_f.Z+maxd);
		
		f32 min_y = lwn_f.Y + 0.5*BS;
		if(position.Y < min_y)
		{
			position.Y = min_y;

			//v3f old_speed = m_speed;

			if(m_speed.Y < 0)
				m_speed.Y = 0;

			/*if(collision_info)
			{
				// Report fall collision
				if(old_speed.Y < m_speed.Y - 0.1)
				{
					CollisionInfo info;
					info.t = COLLISION_FALL;
					info.speed = m_speed.Y - old_speed.Y;
					collision_info->push_back(info);
				}
			}*/
		}
	}

	/*
		Calculate player collision box (new and old)
	*/
	core::aabbox3d<f32> playerbox(
		position.X - player_radius,
		position.Y - 0.0,
		position.Z - player_radius,
		position.X + player_radius,
		position.Y + player_height,
		position.Z + player_radius
	);
	core::aabbox3d<f32> playerbox_old(
		oldpos.X - player_radius,
		oldpos.Y - 0.0,
		oldpos.Z - player_radius,
		oldpos.X + player_radius,
		oldpos.Y + player_height,
		oldpos.Z + player_radius
	);

	/*
		If the player's feet touch the topside of any node, this is
		set to true.

		Player is allowed to jump when this is true.
	*/
	touching_ground = false;

	/*std::cout<<"Checking collisions for ("
			<<oldpos_i.X<<","<<oldpos_i.Y<<","<<oldpos_i.Z
			<<") -> ("
			<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z
			<<"):"<<std::endl;*/
	
	bool standing_on_unloaded = false;
	
	/*
		Go through every node around the player
	*/
	for(s16 y = oldpos_i.Y - 1; y <= oldpos_i.Y + 2; y++)
	for(s16 z = oldpos_i.Z - 1; z <= oldpos_i.Z + 1; z++)
	for(s16 x = oldpos_i.X - 1; x <= oldpos_i.X + 1; x++)
	{
		bool is_unloaded = false;
		try{
			// Player collides into walkable nodes
			if(nodemgr->get(map.getNode(v3s16(x,y,z))).walkable == false)
				continue;
		}
		catch(InvalidPositionException &e)
		{
			is_unloaded = true;
			// Doing nothing here will block the player from
			// walking over map borders
		}

		core::aabbox3d<f32> nodebox = getNodeBox(v3s16(x,y,z), BS);
		
		/*
			See if the player is touching ground.

			Player touches ground if player's minimum Y is near node's
			maximum Y and player's X-Z-area overlaps with the node's
			X-Z-area.

			Use 0.15*BS so that it is easier to get on a node.
		*/
		if(
				//fabs(nodebox.MaxEdge.Y-playerbox.MinEdge.Y) < d
				fabs(nodebox.MaxEdge.Y-playerbox.MinEdge.Y) < 0.15*BS
				&& nodebox.MaxEdge.X-d > playerbox.MinEdge.X
				&& nodebox.MinEdge.X+d < playerbox.MaxEdge.X
				&& nodebox.MaxEdge.Z-d > playerbox.MinEdge.Z
				&& nodebox.MinEdge.Z+d < playerbox.MaxEdge.Z
		){
			touching_ground = true;
			if(is_unloaded)
				standing_on_unloaded = true;
		}
		
		// If player doesn't intersect with node, ignore node.
		if(playerbox.intersectsWithBox(nodebox) == false)
			continue;
		
		/*
			Go through every axis
		*/
		v3f dirs[3] = {
			v3f(0,0,1), // back-front
			v3f(0,1,0), // top-bottom
			v3f(1,0,0), // right-left
		};
		for(u16 i=0; i<3; i++)
		{
			/*
				Calculate values along the axis
			*/
			f32 nodemax = nodebox.MaxEdge.dotProduct(dirs[i]);
			f32 nodemin = nodebox.MinEdge.dotProduct(dirs[i]);
			f32 playermax = playerbox.MaxEdge.dotProduct(dirs[i]);
			f32 playermin = playerbox.MinEdge.dotProduct(dirs[i]);
			f32 playermax_old = playerbox_old.MaxEdge.dotProduct(dirs[i]);
			f32 playermin_old = playerbox_old.MinEdge.dotProduct(dirs[i]);
			
			/*
				Check collision for the axis.
				Collision happens when player is going through a surface.
			*/
			/*f32 neg_d = d;
			f32 pos_d = d;
			// Make it easier to get on top of a node
			if(i == 1)
				neg_d = 0.15*BS;
			bool negative_axis_collides =
				(nodemax > playermin && nodemax <= playermin_old + neg_d
					&& m_speed.dotProduct(dirs[i]) < 0);
			bool positive_axis_collides =
				(nodemin < playermax && nodemin >= playermax_old - pos_d
					&& m_speed.dotProduct(dirs[i]) > 0);*/
			bool negative_axis_collides =
				(nodemax > playermin && nodemax <= playermin_old + d
					&& m_speed.dotProduct(dirs[i]) < 0);
			bool positive_axis_collides =
				(nodemin < playermax && nodemin >= playermax_old - d
					&& m_speed.dotProduct(dirs[i]) > 0);
			bool main_axis_collides =
					negative_axis_collides || positive_axis_collides;
			
			/*
				Check overlap of player and node in other axes
			*/
			bool other_axes_overlap = true;
			for(u16 j=0; j<3; j++)
			{
				if(j == i)
					continue;
				f32 nodemax = nodebox.MaxEdge.dotProduct(dirs[j]);
				f32 nodemin = nodebox.MinEdge.dotProduct(dirs[j]);
				f32 playermax = playerbox.MaxEdge.dotProduct(dirs[j]);
				f32 playermin = playerbox.MinEdge.dotProduct(dirs[j]);
				if(!(nodemax - d > playermin && nodemin + d < playermax))
				{
					other_axes_overlap = false;
					break;
				}
			}
			
			/*
				If this is a collision, revert the position in the main
				direction.
			*/
			if(other_axes_overlap && main_axis_collides)
			{
				//v3f old_speed = m_speed;

				m_speed -= m_speed.dotProduct(dirs[i]) * dirs[i];
				position -= position.dotProduct(dirs[i]) * dirs[i];
				position += oldpos.dotProduct(dirs[i]) * dirs[i];
				
				/*if(collision_info)
				{
					// Report fall collision
					if(old_speed.Y < m_speed.Y - 0.1)
					{
						CollisionInfo info;
						info.t = COLLISION_FALL;
						info.speed = m_speed.Y - old_speed.Y;
						collision_info->push_back(info);
					}
				}*/
			}
		
		}
	} // xyz

	/*
		Check the nodes under the player to see from which node the
		player is sneaking from, if any.
	*/
	{
		v3s16 pos_i_bottom = floatToInt(position - v3f(0,BS/2,0), BS);
		v2f player_p2df(position.X, position.Z);
		f32 min_distance_f = 100000.0*BS;
		// If already seeking from some node, compare to it.
		/*if(m_sneak_node_exists)
		{
			v3f sneaknode_pf = intToFloat(m_sneak_node, BS);
			v2f sneaknode_p2df(sneaknode_pf.X, sneaknode_pf.Z);
			f32 d_horiz_f = player_p2df.getDistanceFrom(sneaknode_p2df);
			f32 d_vert_f = fabs(sneaknode_pf.Y + BS*0.5 - position.Y);
			// Ignore if player is not on the same level (likely dropped)
			if(d_vert_f < 0.15*BS)
				min_distance_f = d_horiz_f;
		}*/
		v3s16 new_sneak_node = m_sneak_node;
		for(s16 x=-1; x<=1; x++)
		for(s16 z=-1; z<=1; z++)
		{
			v3s16 p = pos_i_bottom + v3s16(x,0,z);
			v3f pf = intToFloat(p, BS);
			v2f node_p2df(pf.X, pf.Z);
			f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
			f32 max_axis_distance_f = MYMAX(
					fabs(player_p2df.X-node_p2df.X),
					fabs(player_p2df.Y-node_p2df.Y));
					
			if(distance_f > min_distance_f ||
					max_axis_distance_f > 0.5*BS + sneak_max + 0.1*BS)
				continue;

			try{
				// The node to be sneaked on has to be walkable
				if(nodemgr->get(map.getNode(p)).walkable == false)
					continue;
				// And the node above it has to be nonwalkable
				if(nodemgr->get(map.getNode(p+v3s16(0,1,0))).walkable == true)
					continue;
			}
			catch(InvalidPositionException &e)
			{
				continue;
			}

			min_distance_f = distance_f;
			new_sneak_node = p;
		}
		
		bool sneak_node_found = (min_distance_f < 100000.0*BS*0.9);
		
		if(control.sneak && m_sneak_node_exists)
		{
			if(sneak_node_found)
				m_sneak_node = new_sneak_node;
		}
		else
		{
			m_sneak_node = new_sneak_node;
			m_sneak_node_exists = sneak_node_found;
		}

		/*
			If sneaking, the player's collision box can be in air, so
			this has to be set explicitly
		*/
		if(sneak_node_found && control.sneak)
			touching_ground = true;
	}
	
	/*
		Set new position
	*/
	setPosition(position);
	
	/*
		Report collisions
	*/
	if(collision_info)
	{
		// Report fall collision
		if(old_speed.Y < m_speed.Y - 0.1 && !standing_on_unloaded)
		{
			CollisionInfo info;
			info.t = COLLISION_FALL;
			info.speed = m_speed.Y - old_speed.Y;
			collision_info->push_back(info);
		}
	}
}
				<< "unsupported ser_fmt_ver"<< std::endl;
		return;
	}

	m_server_ser_ver = server_ser_ver;

	// We can be totally wrong with this guess
	// but we only need some value < 25.
	m_proto_ver = 24;

	// Get player position
	v3s16 playerpos_s16(0, BS * 2 + BS * 20, 0);
	if (pkt->getSize() >= 1 + 6) {
		*pkt >> playerpos_s16;
	}
	v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS / 2, 0);


	// Set player position
	Player *player = m_env.getLocalPlayer();
	assert(player != NULL);
	player->setPosition(playerpos_f);

	if (pkt->getSize() >= 1 + 6 + 8) {
		// Get map seed
		*pkt >> m_map_seed;
		infostream << "Client: received map seed: " << m_map_seed << std::endl;
	}

	if (pkt->getSize() >= 1 + 6 + 8 + 4) {
		*pkt >> m_recommended_send_interval;
Esempio n. 5
0
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()),
					};
					
					// This fixes a strange bug
					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);
					}

					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);

				TileSpec tile = getNodeTileN(n, p, 0, data);
				tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
				tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;

				TileSpec tile_top = getNodeTileN(n, p, 1, data);
				tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
				tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;

				AtlasPointer ap = tile.texture;
				AtlasPointer ap_top = tile_top.texture;

				video::SColor c(255,255,255,255);

				u16 indices[] = {0,1,2,2,3,0};

				//FRONT
				video::S3DVertex vertices_front[4] =
				{
					video::S3DVertex(-BS/16,-BS/2,BS/16, 0,0,0, c,
							ap.x0(), ap.y1()),
					video::S3DVertex(BS/16,-BS/2,BS/16, 0,0,0, c,
							ap.x1(), ap.y1()),
					video::S3DVertex(BS/10,BS/10,BS/10, 0,0,0, c,
							ap.x1(), ap.y0()),
					video::S3DVertex(-BS/10,BS/10,BS/10, 0,0,0, c,
							ap.x0(), ap.y0()),
				};

				//BACK
				video::S3DVertex vertices_back[4] =
				{
					video::S3DVertex(-BS/16,-BS/2,-BS/16, 0,0,0, c,
							ap.x0(), ap.y1()),
					video::S3DVertex(BS/16,-BS/2,-BS/16, 0,0,0, c,
							ap.x1(), ap.y1()),
					video::S3DVertex(BS/10,BS/10,-BS/10, 0,0,0, c,
							ap.x1(), ap.y0()),
					video::S3DVertex(-BS/10,BS/10,-BS/10, 0,0,0, c,
							ap.x0(), ap.y0()),
				};

				//RIGHT
				video::S3DVertex vertices_right[4] =
				{
					video::S3DVertex(-BS/16,-BS/2,-BS/16, 0,0,0, c,
							ap.x0(), ap.y1()),
					video::S3DVertex(-BS/16,-BS/2,BS/16, 0,0,0, c,
							ap.x1(), ap.y1()),
					video::S3DVertex(-BS/10,BS/10,BS/10, 0,0,0, c,
							ap.x1(), ap.y0()),
					video::S3DVertex(-BS/10,BS/10,-BS/10, 0,0,0, c,
							ap.x0(), ap.y0()),
				};

				//LEFT
				video::S3DVertex vertices_left[4] =
				{
					video::S3DVertex(BS/16,-BS/2,-BS/16, 0,0,0, c,
							ap.x0(), ap.y1()),
					video::S3DVertex(BS/16,-BS/2,BS/16, 0,0,0, c,
							ap.x1(), ap.y1()),
					video::S3DVertex(BS/10,BS/10,BS/10, 0,0,0, c,
							ap.x1(), ap.y0()),
					video::S3DVertex(BS/10,BS/10,-BS/10, 0,0,0, c,
							ap.x0(), ap.y0()),
				};

				//TOP
				video::S3DVertex vertices_top[4] =
				{
					video::S3DVertex(BS/10,BS/10,-BS/10, 0,0,0, c,
							ap_top.x0(), ap_top.y1()),
					video::S3DVertex(BS/10,BS/10,BS/10, 0,0,0, c,
							ap_top.x1(), ap_top.y1()),
					video::S3DVertex(-BS/10,BS/10,BS/10, 0,0,0, c,
							ap_top.x1(), ap_top.y0()),
					video::S3DVertex(-BS/10,BS/10,-BS/10, 0,0,0, c,
							ap_top.x0(), ap_top.y0()),
				};

				//BOTTOM
				video::S3DVertex vertices_bottom[4] =
				{
					video::S3DVertex(BS/16,-BS/2,-BS/16, 0,0,0, c,
							ap.x0(), ap.y1()),
					video::S3DVertex(BS/16,-BS/2,BS/16, 0,0,0, c,
							ap.x1(), ap.y1()),
					video::S3DVertex(-BS/16,-BS/2,BS/16, 0,0,0, c,
							ap.x1(), ap.y0()),
					video::S3DVertex(-BS/16,-BS/2,-BS/16, 0,0,0, c,
							ap.x0(), ap.y0()),
				};

				v3f move_torch(0,0,0);
				int xyrotation = 0;
				int yzrotation = 0;
				for(s32 i=0; i<4; i++)
				{
					if(dir == v3s16(0,1,0))
					{
						xyrotation = 180;
					}
					if(dir == v3s16(0,0,1))
					{
						yzrotation= -45;
						move_torch = v3f(0,0,0.2*BS);
					}
					if(dir == v3s16(0,0,-1))
					{
						yzrotation= 45;
						move_torch = v3f(0,0,-0.2*BS);
					}
					if(dir == v3s16(1,0,0))
					{
						xyrotation= 45;
						move_torch = v3f(0.2*BS,0,0);
					}
					if(dir == v3s16(-1,0,0))
					{
						xyrotation= -45;
						move_torch = v3f(-0.2*BS,0,0);
					}
					
					vertices_front[i].Pos.rotateXYBy(xyrotation);
					vertices_back[i].Pos.rotateXYBy(xyrotation);
					vertices_left[i].Pos.rotateXYBy(xyrotation);
					vertices_right[i].Pos.rotateXYBy(xyrotation);
					vertices_top[i].Pos.rotateXYBy(xyrotation);
					vertices_bottom[i].Pos.rotateXYBy(xyrotation);

					vertices_front[i].Pos.rotateYZBy(yzrotation);
					vertices_back[i].Pos.rotateYZBy(yzrotation);
					vertices_left[i].Pos.rotateYZBy(yzrotation);
					vertices_right[i].Pos.rotateYZBy(yzrotation);
					vertices_top[i].Pos.rotateYZBy(yzrotation);
					vertices_bottom[i].Pos.rotateYZBy(yzrotation);

					vertices_front[i].Pos += intToFloat(p, BS) + move_torch;
					vertices_back[i].Pos += intToFloat(p, BS) + move_torch;
					vertices_left[i].Pos += intToFloat(p, BS) + move_torch;
					vertices_right[i].Pos += intToFloat(p, BS) + move_torch;
					vertices_top[i].Pos += intToFloat(p, BS) + move_torch;
					vertices_bottom[i].Pos += intToFloat(p, BS) + move_torch;
					move_torch = v3f(0,0,0);
				}
				collector.append(tile, vertices_front, 4, indices, 6);
				collector.append(tile, vertices_back, 4, indices, 6);
				collector.append(tile, vertices_left, 4, indices, 6);
				collector.append(tile, vertices_right, 4, indices, 6);
				collector.append(tile, vertices_top, 4, indices, 6);
				collector.append(tile, vertices_bottom, 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)
				if(f2->walkable)
				{
					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)
				if(f2->walkable)
				{
					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_MESECONLIKE:
			{
				bool is_mesecon_x [] = { false, false };  /* x-1, x+1 */
				bool is_mesecon_z [] = { false, false };  /* z-1, z+1 */

				bool is_mesecon_z_minus_y [] = { false, false };  /* z-1, z+1; y-1 */
				bool is_mesecon_x_minus_y [] = { false, false };  /* x-1, z+1; y-1 */
				bool is_mesecon_z_plus_y [] = { false, false };  /* z-1, z+1; y+1 */
				bool is_mesecon_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_mesecon_x[0] = true;
				}
				if(n_minus_x_minus_y.getContent() == thiscontent)
				{
					is_mesecon_x_minus_y[0] = true;
				}
				if(n_minus_x_plus_y.getContent() == thiscontent)
				{
					is_mesecon_x_plus_y[0] = true;
				}

				if(n_plus_x.getContent() == thiscontent)
				{
					is_mesecon_x[1] = true;
				}
				if(n_plus_x_minus_y.getContent() == thiscontent)
				{
					is_mesecon_x_minus_y[1] = true;
				}
				if(n_plus_x_plus_y.getContent() == thiscontent)
				{
					is_mesecon_x_plus_y[1] = true;
				}

				if(n_minus_z.getContent() == thiscontent)
				{
					is_mesecon_z[0] = true;
				}
				if(n_minus_z_minus_y.getContent() == thiscontent)
				{
					is_mesecon_z_minus_y[0] = true;
				}
				if(n_minus_z_plus_y.getContent() == thiscontent)
				{
					is_mesecon_z_plus_y[0] = true;
				}

				if(n_plus_z.getContent() == thiscontent)
				{
					is_mesecon_z[1] = true;
				}
				if(n_plus_z_minus_y.getContent() == thiscontent)
				{
					is_mesecon_z_minus_y[1] = true;
				}
				if(n_plus_z_plus_y.getContent() == thiscontent)
				{
					is_mesecon_z_plus_y[1] = true;
				}


				bool is_mesecon_x_all[] = {false, false};
				bool is_mesecon_z_all[] = {false, false};
				is_mesecon_x_all[0] = is_mesecon_x[0] || is_mesecon_x_minus_y[0] || is_mesecon_x_plus_y[0];
				is_mesecon_x_all[1] = is_mesecon_x[1] || is_mesecon_x_minus_y[1] || is_mesecon_x_plus_y[1];
				is_mesecon_z_all[0] = is_mesecon_z[0] || is_mesecon_z_minus_y[0] || is_mesecon_z_plus_y[0];
				is_mesecon_z_all[1] = is_mesecon_z[1] || is_mesecon_z_minus_y[1] || is_mesecon_z_plus_y[1];

				bool is_straight = (is_mesecon_x_all[0] && is_mesecon_x_all[1] && !is_mesecon_z_all[1] && !is_mesecon_z_all[0]) || (is_mesecon_z_all[0] && is_mesecon_z_all[1] && !is_mesecon_x_all[1] && !is_mesecon_x_all[0]);//is really straight, mesecons on both sides
				int adjacencies = is_mesecon_x_all[0] + is_mesecon_x_all[1] + is_mesecon_z_all[0] + is_mesecon_z_all[1];


				// Assign textures
				u8 tileindex = 0; // straight
				if(adjacencies < 2)
					tileindex = 0; // straight
				else if(adjacencies == 2)
				{
					if(is_straight)
					{
						tileindex = 0; // straight
					}
					else
					{
						tileindex = 1; // curved
					}
				}
				else if(adjacencies == 3)
				{
					tileindex = 2; // t-junction
				}
				else if(adjacencies == 4)
				{
					tileindex = 3; // crossing
				}

				TileSpec tile = getNodeTileN(n, p, tileindex, data);
				tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
				tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;

				TileSpec tile_slope = getNodeTileN(n, p, 0, data);
				tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
				tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;

				AtlasPointer ap = tile.texture;
				AtlasPointer ap_slope = tile_slope.texture;
				
				u16 l = getInteriorLight(n, 0, data);
				video::SColor c = MapBlock_LightColor(255, l);

				float d = (float)BS/64;

				u16 indices[] = {0,1,2,2,3,0};

				if(is_mesecon_x_plus_y[0] || is_mesecon_x_plus_y[1] || is_mesecon_z_plus_y[0] || is_mesecon_z_plus_y[1])
				{
					video::S3DVertex vertices_slope[4] =
					{
						video::S3DVertex(BS/2,BS/2+d,BS/2-d, 0,0,0, c,
								ap_slope.x1(), ap_slope.y1()),
						video::S3DVertex(-BS/2,BS/2+d,BS/2-d, 0,0,0, c,
								ap_slope.x0(), ap_slope.y1()),
						video::S3DVertex(-BS/2,-BS/2,BS/2-d, 0,0,0, c,
								ap_slope.x0(), ap_slope.y0()),
						video::S3DVertex(BS/2,-BS/2,BS/2-d, 0,0,0, c,
								ap_slope.x1(), ap_slope.y0()),
					};
					video::S3DVertex vertices_slope_xplus1[4];
					video::S3DVertex vertices_slope_xplus0[4];
					video::S3DVertex vertices_slope_zplus0[4];
					video::S3DVertex vertices_slope_zplus1[4];
					for(s32 i=0; i<4; i++)
					{
						if(is_mesecon_x_plus_y[1])
						{
							vertices_slope_xplus1[i] = vertices_slope[i];
							vertices_slope_xplus1[i].Pos.rotateXZBy(-90);
						}
						if(is_mesecon_x_plus_y[0])
						{
							vertices_slope_xplus0[i] = vertices_slope[i];
							vertices_slope_xplus0[i].Pos.rotateXZBy(90);
						}
						if(is_mesecon_z_plus_y[0])
						{
							vertices_slope_zplus0[i] = vertices_slope[i];
							vertices_slope_zplus0[i].Pos.rotateXZBy(180);
						}
						if(is_mesecon_z_plus_y[1])
						{
							vertices_slope_zplus1[i] = vertices_slope[i];
							vertices_slope_zplus1[i].Pos.rotateXZBy(0);
						}
						vertices_slope_xplus1[i].Pos += intToFloat(p, BS);
						vertices_slope_xplus0[i].Pos += intToFloat(p, BS);
						vertices_slope_zplus1[i].Pos += intToFloat(p, BS);
						vertices_slope_zplus0[i].Pos += intToFloat(p, BS);
					}
					if(is_mesecon_x_plus_y[1])
					{
						collector.append(tile, vertices_slope_xplus1, 4, indices, 6);
					}
					if(is_mesecon_x_plus_y[0])
					{
						collector.append(tile, vertices_slope_xplus0, 4, indices, 6);
					}
					if(is_mesecon_z_plus_y[1])
					{
						collector.append(tile, vertices_slope_zplus1, 4, indices, 6);
					}
					if(is_mesecon_z_plus_y[0])
					{
						collector.append(tile, vertices_slope_zplus0, 4, indices, 6);
					}
				}

				video::S3DVertex vertices[4] =
				{
						video::S3DVertex(-BS/2-d,-BS/2+d,-BS/2-d, 0,0,0, c,
								ap.x0(), ap.y1()),
						video::S3DVertex(BS/2+d,-BS/2+d,-BS/2-d, 0,0,0, c,
								ap.x1(), ap.y1()),
						video::S3DVertex(BS/2+d,-BS/2+d,BS/2+d, 0,0,0, c,
								ap.x1(), ap.y0()),
						video::S3DVertex(-BS/2-d,-BS/2+d,BS/2+d, 0,0,0, c,
								ap.x0(), ap.y0()),
				};

				// Rotate textures
				int angle = 0;

				if(adjacencies == 1)
				{
					if(is_mesecon_x_all[0] || is_mesecon_x_all[1])
					{
						angle = 90;
					}
				}
				if(adjacencies == 2)
				{
					if(is_mesecon_x_all[0] && is_mesecon_x_all[1])
					{
						angle = 90;
					}
					if(is_mesecon_z_all[0] && is_mesecon_z_all[1])
					{
						if (n_minus_z_plus_y.getContent() == thiscontent)
						{
							angle = 180;
						}
					}
					else if(is_mesecon_x_all[0] && is_mesecon_z_all[0])
					{
						angle = 270;
					}
					else if(is_mesecon_x_all[0] && is_mesecon_z_all[1])
					{
						angle = 180;
					}
					else if(is_mesecon_x_all[1] && is_mesecon_z_all[1])
					{
						angle = 90;
					}
				}
				if(adjacencies == 3)
				{
					if(!is_mesecon_x_all[0])
					{
						angle=0;
					}
					if(!is_mesecon_x_all[1])
					{
						angle=180;
					}
					if(!is_mesecon_z_all[0])
					{
						angle=90;
					}
					if(!is_mesecon_z_all[1])
					{
						angle=270;
					}
				}

				if(angle != 0)
				{
					for(u16 i=0; i<4; i++)
					{
						vertices[i].Pos.rotateXZBy(angle);
					}
				}

				for(s32 i=0; i<4; i++)
				{
					vertices[i].Pos += intToFloat(p, BS);
				}

				collector.append(tile, vertices, 4, indices, 6);
				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];

				bool is_straight = (is_rail_x_all[0] && is_rail_x_all[1]) || (is_rail_z_all[0] && is_rail_z_all[1]);//is really straight, rails on both sides
				int adjacencies = is_rail_x_all[0] + is_rail_x_all[1] + is_rail_z_all[0] + is_rail_z_all[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]) //is straight because sloped
				{
					adjacencies = 5; //5 means sloped
					is_straight = true;
				}

				// Assign textures
				u8 tileindex = 0; // straight
				if(adjacencies < 2)
				{
					tileindex = 0; // straight
				}
				else if(adjacencies == 2)
				{
					if(is_straight)
					{
						tileindex = 0; // straight
					}
					else
					{
						tileindex = 1; // curved
					}
				}
				else if(adjacencies == 3)
				{
					tileindex = 2; // t-junction
				}
				else if(adjacencies == 4)
				{
					tileindex = 3; // crossing
				}

				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()),
				};


				// Rotate textures
				int angle = 0;

				if(adjacencies == 1)
				{
					if(is_rail_x_all[0] || is_rail_x_all[1])
					{
						angle = 90;
					}
				}
				if(adjacencies == 2)
				{
					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;
					}
				}
				if(adjacencies == 3)
				{
					if(!is_rail_x_all[0])
					{
						angle = 0;
					}
					if(!is_rail_x_all[1])
					{
						angle = 180;
					}
					if(!is_rail_z_all[0])
					{
						angle = 90;
					}
					if(!is_rail_z_all[1])
					{
						angle = 270;
					}
				}
				//adjacencies 4: Crossing
				if(adjacencies == 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;
					}
				}

				if(angle != 0)
				{
					for(u16 i=0; i<4; i++)
					{
						vertices[i].Pos.rotateXZBy(angle);
					}
				}

				for(s32 i=0; i<4; i++)
				{
					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;}		
		}
	}
}
Esempio n. 6
0
void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime,
		f32 tool_reload_ratio, ClientEnvironment &c_env)
{
	// Get player position
	// Smooth the movement when walking up stairs
	v3f old_player_position = m_playernode->getPosition();
	v3f player_position = player->getPosition();
	if (player->isAttached && player->parent)
		player_position = player->parent->getPosition();
	//if(player->touching_ground && player_position.Y > old_player_position.Y)
	if(player->touching_ground &&
			player_position.Y > old_player_position.Y)
	{
		f32 oldy = old_player_position.Y;
		f32 newy = player_position.Y;
		f32 t = exp(-23*frametime);
		player_position.Y = oldy * t + newy * (1-t);
	}

	// Set player node transformation
	m_playernode->setPosition(player_position);
	m_playernode->setRotation(v3f(0, -1 * player->getYaw(), 0));
	m_playernode->updateAbsolutePosition();

	// Get camera tilt timer (hurt animation)
	float cameratilt = fabs(fabs(player->hurt_tilt_timer-0.75)-0.75);

	// Fall bobbing animation
	float fall_bobbing = 0;
	if(player->camera_impact >= 1 && m_camera_mode < CAMERA_MODE_THIRD)
	{
		if(m_view_bobbing_fall == -1) // Effect took place and has finished
			player->camera_impact = m_view_bobbing_fall = 0;
		else if(m_view_bobbing_fall == 0) // Initialize effect
			m_view_bobbing_fall = 1;

		// Convert 0 -> 1 to 0 -> 1 -> 0
		fall_bobbing = m_view_bobbing_fall < 0.5 ? m_view_bobbing_fall * 2 : -(m_view_bobbing_fall - 0.5) * 2 + 1;
		// Smoothen and invert the above
		fall_bobbing = sin(fall_bobbing * 0.5 * M_PI) * -1;
		// Amplify according to the intensity of the impact
		fall_bobbing *= (1 - rangelim(50 / player->camera_impact, 0, 1)) * 5;

		fall_bobbing *= m_cache_fall_bobbing_amount;
	}

	// Calculate players eye offset for different camera modes
	v3f PlayerEyeOffset = player->getEyeOffset();
	if (m_camera_mode == CAMERA_MODE_FIRST)
		PlayerEyeOffset += player->eye_offset_first;
	else
		PlayerEyeOffset += player->eye_offset_third;

	// Set head node transformation
	m_headnode->setPosition(PlayerEyeOffset+v3f(0,cameratilt*-player->hurt_tilt_strength+fall_bobbing,0));
	m_headnode->setRotation(v3f(player->getPitch(), 0, cameratilt*player->hurt_tilt_strength));
	m_headnode->updateAbsolutePosition();

	// Compute relative camera position and target
	v3f rel_cam_pos = v3f(0,0,0);
	v3f rel_cam_target = v3f(0,0,1);
	v3f rel_cam_up = v3f(0,1,0);

	if (m_view_bobbing_anim != 0 && m_camera_mode < CAMERA_MODE_THIRD)
	{
		f32 bobfrac = my_modf(m_view_bobbing_anim * 2);
		f32 bobdir = (m_view_bobbing_anim < 0.5) ? 1.0 : -1.0;

		#if 1
		f32 bobknob = 1.2;
		f32 bobtmp = sin(pow(bobfrac, bobknob) * M_PI);
		//f32 bobtmp2 = cos(pow(bobfrac, bobknob) * M_PI);

		v3f bobvec = v3f(
			0.3 * bobdir * sin(bobfrac * M_PI),
			-0.28 * bobtmp * bobtmp,
			0.);

		//rel_cam_pos += 0.2 * bobvec;
		//rel_cam_target += 0.03 * bobvec;
		//rel_cam_up.rotateXYBy(0.02 * bobdir * bobtmp * M_PI);
		float f = 1.0;
		f *= m_cache_view_bobbing_amount;
		rel_cam_pos += bobvec * f;
		//rel_cam_target += 0.995 * bobvec * f;
		rel_cam_target += bobvec * f;
		rel_cam_target.Z -= 0.005 * bobvec.Z * f;
		//rel_cam_target.X -= 0.005 * bobvec.X * f;
		//rel_cam_target.Y -= 0.005 * bobvec.Y * f;
		rel_cam_up.rotateXYBy(-0.03 * bobdir * bobtmp * M_PI * f);
		#else
		f32 angle_deg = 1 * bobdir * sin(bobfrac * M_PI);
		f32 angle_rad = angle_deg * M_PI / 180;
		f32 r = 0.05;
		v3f off = v3f(
			r * sin(angle_rad),
			r * (cos(angle_rad) - 1),
			0);
		rel_cam_pos += off;
		//rel_cam_target += off;
		rel_cam_up.rotateXYBy(angle_deg);
		#endif

	}

	// Compute absolute camera position and target
	m_headnode->getAbsoluteTransformation().transformVect(m_camera_position, rel_cam_pos);
	m_headnode->getAbsoluteTransformation().rotateVect(m_camera_direction, rel_cam_target - rel_cam_pos);

	v3f abs_cam_up;
	m_headnode->getAbsoluteTransformation().rotateVect(abs_cam_up, rel_cam_up);

	// Seperate camera position for calculation
	v3f my_cp = m_camera_position;

	// Reposition the camera for third person view
	if (m_camera_mode > CAMERA_MODE_FIRST)
	{
		if (m_camera_mode == CAMERA_MODE_THIRD_FRONT)
			m_camera_direction *= -1;

		my_cp.Y += 2;

		// Calculate new position
		bool abort = false;
		for (int i = BS; i <= BS*2.75; i++)
		{
			my_cp.X = m_camera_position.X + m_camera_direction.X*-i;
			my_cp.Z = m_camera_position.Z + m_camera_direction.Z*-i;
			if (i > 12)
				my_cp.Y = m_camera_position.Y + (m_camera_direction.Y*-i);

			// Prevent camera positioned inside nodes
			INodeDefManager *nodemgr = m_gamedef->ndef();
			MapNode n = c_env.getClientMap().getNodeNoEx(floatToInt(my_cp, BS));
			const ContentFeatures& features = nodemgr->get(n);
			if(features.walkable)
			{
				my_cp.X += m_camera_direction.X*-1*-BS/2;
				my_cp.Z += m_camera_direction.Z*-1*-BS/2;
				my_cp.Y += m_camera_direction.Y*-1*-BS/2;
				abort = true;
				break;
			}
		}

		// If node blocks camera position don't move y to heigh
		if (abort && my_cp.Y > player_position.Y+BS*2)
			my_cp.Y = player_position.Y+BS*2;
	}

	// Update offset if too far away from the center of the map
	m_camera_offset.X += CAMERA_OFFSET_STEP*
			(((s16)(my_cp.X/BS) - m_camera_offset.X)/CAMERA_OFFSET_STEP);
	m_camera_offset.Y += CAMERA_OFFSET_STEP*
			(((s16)(my_cp.Y/BS) - m_camera_offset.Y)/CAMERA_OFFSET_STEP);
	m_camera_offset.Z += CAMERA_OFFSET_STEP*
			(((s16)(my_cp.Z/BS) - m_camera_offset.Z)/CAMERA_OFFSET_STEP);

	// Set camera node transformation
	m_cameranode->setPosition(my_cp-intToFloat(m_camera_offset, BS));
	m_cameranode->setUpVector(abs_cam_up);
	// *100.0 helps in large map coordinates
	m_cameranode->setTarget(my_cp-intToFloat(m_camera_offset, BS) + 100 * m_camera_direction);

	// update the camera position in front-view mode to render blocks behind player
	if (m_camera_mode == CAMERA_MODE_THIRD_FRONT)
		m_camera_position = my_cp;

	// Get FOV setting
	f32 fov_degrees = m_cache_fov;
	fov_degrees = MYMAX(fov_degrees, 10.0);
	fov_degrees = MYMIN(fov_degrees, 170.0);

	// FOV and aspect ratio
	m_aspect = (f32) porting::getWindowSize().X / (f32) porting::getWindowSize().Y;
	m_fov_y = fov_degrees * M_PI / 180.0;
	// Increase vertical FOV on lower aspect ratios (<16:10)
	m_fov_y *= MYMAX(1.0, MYMIN(1.4, sqrt(16./10. / m_aspect)));
	m_fov_x = 2 * atan(m_aspect * tan(0.5 * m_fov_y));
	m_cameranode->setAspectRatio(m_aspect);
	m_cameranode->setFOV(m_fov_y);

	// Position the wielded item
	//v3f wield_position = v3f(45, -35, 65);
	v3f wield_position = v3f(55, -35, 65);
	//v3f wield_rotation = v3f(-100, 120, -100);
	v3f wield_rotation = v3f(-100, 120, -100);
	wield_position.Y += fabs(m_wield_change_timer)*320 - 40;
	if(m_digging_anim < 0.05 || m_digging_anim > 0.5)
	{
		f32 frac = 1.0;
		if(m_digging_anim > 0.5)
			frac = 2.0 * (m_digging_anim - 0.5);
		// This value starts from 1 and settles to 0
		f32 ratiothing = pow((1.0f - tool_reload_ratio), 0.5f);
		//f32 ratiothing2 = pow(ratiothing, 0.5f);
		f32 ratiothing2 = (easeCurve(ratiothing*0.5))*2.0;
		wield_position.Y -= frac * 25.0 * pow(ratiothing2, 1.7f);
		//wield_position.Z += frac * 5.0 * ratiothing2;
		wield_position.X -= frac * 35.0 * pow(ratiothing2, 1.1f);
		wield_rotation.Y += frac * 70.0 * pow(ratiothing2, 1.4f);
		//wield_rotation.X -= frac * 15.0 * pow(ratiothing2, 1.4f);
		//wield_rotation.Z += frac * 15.0 * pow(ratiothing2, 1.0f);
	}
	if (m_digging_button != -1)
	{
		f32 digfrac = m_digging_anim;
		wield_position.X -= 50 * sin(pow(digfrac, 0.8f) * M_PI);
		wield_position.Y += 24 * sin(digfrac * 1.8 * M_PI);
		wield_position.Z += 25 * 0.5;

		// Euler angles are PURE EVIL, so why not use quaternions?
		core::quaternion quat_begin(wield_rotation * core::DEGTORAD);
		core::quaternion quat_end(v3f(80, 30, 100) * core::DEGTORAD);
		core::quaternion quat_slerp;
		quat_slerp.slerp(quat_begin, quat_end, sin(digfrac * M_PI));
		quat_slerp.toEuler(wield_rotation);
		wield_rotation *= core::RADTODEG;
	} else {
		f32 bobfrac = my_modf(m_view_bobbing_anim);
		wield_position.X -= sin(bobfrac*M_PI*2.0) * 3.0;
		wield_position.Y += sin(my_modf(bobfrac*2.0)*M_PI) * 3.0;
	}
	m_wieldnode->setPosition(wield_position);
	m_wieldnode->setRotation(wield_rotation);

	m_wieldnode->setColor(player->light_color);

	// Render distance feedback loop
	updateViewingRange(frametime, busytime);

	// If the player is walking, swimming, or climbing,
	// view bobbing is enabled and free_move is off,
	// start (or continue) the view bobbing animation.
	v3f speed = player->getSpeed();
	const bool movement_XZ = hypot(speed.X, speed.Z) > BS;
	const bool movement_Y = fabs(speed.Y) > BS;

	const bool walking = movement_XZ && player->touching_ground;
	const bool swimming = (movement_XZ || player->swimming_vertical) && player->in_liquid;
	const bool climbing = movement_Y && player->is_climbing;
	if ((walking || swimming || climbing) &&
			m_cache_view_bobbing &&
			(!g_settings->getBool("free_move") || !m_gamedef->checkLocalPrivilege("fly")))
	{
		// Start animation
		m_view_bobbing_state = 1;
		m_view_bobbing_speed = MYMIN(speed.getLength(), 40);
	}
	else if (m_view_bobbing_state == 1)
	{
		// Stop animation
		m_view_bobbing_state = 2;
		m_view_bobbing_speed = 60;
	}
}
Esempio n. 7
0
void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
		std::vector<CollisionInfo> *collision_info)
{
	Map *map = &env->getMap();
	INodeDefManager *nodemgr = m_gamedef->ndef();

	v3f position = getPosition();

	// Copy parent position if local player is attached
	if(isAttached)
	{
		setPosition(overridePosition);
		m_sneak_node_exists = false;
		return;
	}

	// Skip collision detection if noclip mode is used
	bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
	bool noclip = m_gamedef->checkLocalPrivilege("noclip") &&
		g_settings->getBool("noclip");
	bool free_move = noclip && fly_allowed && g_settings->getBool("free_move");
	if (free_move) {
		position += m_speed * dtime;
		setPosition(position);
		m_sneak_node_exists = false;
		return;
	}

	/*
		Collision detection
	*/

	bool is_valid_position;
	MapNode node;
	v3s16 pp;

	/*
		Check if player is in liquid (the oscillating value)
	*/

	// If in liquid, the threshold of coming out is at higher y
	if (in_liquid)
	{
		pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
		node = map->getNodeNoEx(pp, &is_valid_position);
		if (is_valid_position) {
			in_liquid = nodemgr->get(node.getContent()).isLiquid();
			liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
		} else {
			in_liquid = false;
		}
	}
	// If not in liquid, the threshold of going in is at lower y
	else
	{
		pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
		node = map->getNodeNoEx(pp, &is_valid_position);
		if (is_valid_position) {
			in_liquid = nodemgr->get(node.getContent()).isLiquid();
			liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
		} else {
			in_liquid = false;
		}
	}


	/*
		Check if player is in liquid (the stable value)
	*/
	pp = floatToInt(position + v3f(0,0,0), BS);
	node = map->getNodeNoEx(pp, &is_valid_position);
	if (is_valid_position) {
		in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
	} else {
		in_liquid_stable = false;
	}

	/*
	        Check if player is climbing
	*/


	pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
	v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
	node = map->getNodeNoEx(pp, &is_valid_position);
	bool is_valid_position2;
	MapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2);

	if (!(is_valid_position && is_valid_position2)) {
		is_climbing = false;
	} else {
		is_climbing = (nodemgr->get(node.getContent()).climbable
				|| nodemgr->get(node2.getContent()).climbable) && !free_move;
	}


	/*
		Collision uncertainty radius
		Make it a bit larger than the maximum distance of movement
	*/
	//f32 d = pos_max_d * 1.1;
	// A fairly large value in here makes moving smoother
	f32 d = 0.15*BS;

	// This should always apply, otherwise there are glitches
	sanity_check(d > pos_max_d);

	// Maximum distance over border for sneaking
	f32 sneak_max = BS*0.4;

	/*
		If sneaking, keep in range from the last walked node and don't
		fall off from it
	*/
	if (control.sneak && m_sneak_node_exists &&
			!(fly_allowed && g_settings->getBool("free_move")) && !in_liquid &&
			physics_override_sneak) {
		f32 maxd = 0.5 * BS + sneak_max;
		v3f lwn_f = intToFloat(m_sneak_node, BS);
		position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd);
		position.Z = rangelim(position.Z, lwn_f.Z-maxd, lwn_f.Z+maxd);

		if (!is_climbing) {
			// Move up if necessary
			f32 new_y = (lwn_f.Y - 0.5 * BS) + m_sneak_node_bb_ymax;
			if (position.Y < new_y)
				position.Y = new_y;
			/*
				Collision seems broken, since player is sinking when
				sneaking over the edges of current sneaking_node.
				TODO (when fixed): Set Y-speed only to 0 when position.Y < new_y.
			*/
			if (m_speed.Y < 0)
				m_speed.Y = 0;
		}
	}

	// this shouldn't be hardcoded but transmitted from server
	float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);

#ifdef __ANDROID__
	player_stepheight += (0.5 * BS);
#endif

	v3f accel_f = v3f(0,0,0);

	collisionMoveResult result = collisionMoveSimple(env, m_gamedef,
			pos_max_d, m_collisionbox, player_stepheight, dtime,
			position, m_speed, accel_f);

	/*
		If the player's feet touch the topside of any node, this is
		set to true.

		Player is allowed to jump when this is true.
	*/
	bool touching_ground_was = touching_ground;
	touching_ground = result.touching_ground;

    //bool standing_on_unloaded = result.standing_on_unloaded;

	/*
		Check the nodes under the player to see from which node the
		player is sneaking from, if any.  If the node from under
		the player has been removed, the player falls.
	*/
	f32 position_y_mod = 0.05 * BS;
	if (m_sneak_node_bb_ymax > 0)
		position_y_mod = m_sneak_node_bb_ymax - position_y_mod;
	v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS);
	if (m_sneak_node_exists &&
			nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
			m_old_node_below_type != "air") {
		// Old node appears to have been removed; that is,
		// it wasn't air before but now it is
		m_need_to_get_new_sneak_node = false;
		m_sneak_node_exists = false;
	} else if (nodemgr->get(map->getNodeNoEx(current_node)).name != "air") {
		// We are on something, so make sure to recalculate the sneak
		// node.
		m_need_to_get_new_sneak_node = true;
	}

	if (m_need_to_get_new_sneak_node && physics_override_sneak) {
		m_sneak_node_bb_ymax = 0;
		v3s16 pos_i_bottom = floatToInt(position - v3f(0, position_y_mod, 0), BS);
		v2f player_p2df(position.X, position.Z);
		f32 min_distance_f = 100000.0 * BS;
		// If already seeking from some node, compare to it.
		/*if(m_sneak_node_exists)
		{
			v3f sneaknode_pf = intToFloat(m_sneak_node, BS);
			v2f sneaknode_p2df(sneaknode_pf.X, sneaknode_pf.Z);
			f32 d_horiz_f = player_p2df.getDistanceFrom(sneaknode_p2df);
			f32 d_vert_f = fabs(sneaknode_pf.Y + BS*0.5 - position.Y);
			// Ignore if player is not on the same level (likely dropped)
			if(d_vert_f < 0.15*BS)
				min_distance_f = d_horiz_f;
		}*/
		v3s16 new_sneak_node = m_sneak_node;
		for(s16 x=-1; x<=1; x++)
		for(s16 z=-1; z<=1; z++)
		{
			v3s16 p = pos_i_bottom + v3s16(x,0,z);
			v3f pf = intToFloat(p, BS);
			v2f node_p2df(pf.X, pf.Z);
			f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
			f32 max_axis_distance_f = MYMAX(
					fabs(player_p2df.X-node_p2df.X),
					fabs(player_p2df.Y-node_p2df.Y));

			if(distance_f > min_distance_f ||
					max_axis_distance_f > 0.5*BS + sneak_max + 0.1*BS)
				continue;


			// The node to be sneaked on has to be walkable
			node = map->getNodeNoEx(p, &is_valid_position);
			if (!is_valid_position || nodemgr->get(node).walkable == false)
				continue;
			// And the node above it has to be nonwalkable
			node = map->getNodeNoEx(p + v3s16(0,1,0), &is_valid_position);
			if (!is_valid_position || nodemgr->get(node).walkable) {
				continue;
			}
			if (!physics_override_sneak_glitch) {
				node =map->getNodeNoEx(p + v3s16(0,2,0), &is_valid_position);
				if (!is_valid_position || nodemgr->get(node).walkable)
					continue;
			}

			min_distance_f = distance_f;
			new_sneak_node = p;
		}

		bool sneak_node_found = (min_distance_f < 100000.0 * BS * 0.9);

		m_sneak_node = new_sneak_node;
		m_sneak_node_exists = sneak_node_found;

		if (sneak_node_found) {
			f32 cb_max = 0;
			MapNode n = map->getNodeNoEx(m_sneak_node);
			std::vector<aabb3f> nodeboxes = n.getCollisionBoxes(nodemgr);
			for (std::vector<aabb3f>::iterator it = nodeboxes.begin();
					it != nodeboxes.end(); ++it) {
				aabb3f box = *it;
				if (box.MaxEdge.Y > cb_max)
					cb_max = box.MaxEdge.Y;
			}
			m_sneak_node_bb_ymax = cb_max;
		}

		/*
			If sneaking, the player's collision box can be in air, so
			this has to be set explicitly
		*/
		if(sneak_node_found && control.sneak)
			touching_ground = true;
	}

	/*
		Set new position
	*/
	setPosition(position);

	/*
		Report collisions
	*/

	// Dont report if flying
	if(collision_info && !(g_settings->getBool("free_move") && fly_allowed)) {
		for(size_t i=0; i<result.collisions.size(); i++) {
			const CollisionInfo &info = result.collisions[i];
			collision_info->push_back(info);
		}
	}

	if(!result.standing_on_object && !touching_ground_was && touching_ground) {
		MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
		m_gamedef->event()->put(e);

		// Set camera impact value to be used for view bobbing
		camera_impact = getSpeed().Y * -1;
	}

	{
		camera_barely_in_ceiling = false;
		v3s16 camera_np = floatToInt(getEyePosition(), BS);
		MapNode n = map->getNodeNoEx(camera_np);
		if(n.getContent() != CONTENT_IGNORE){
			if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
				camera_barely_in_ceiling = true;
			}
		}
	}

	/*
		Update the node last under the player
	*/
	m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
	m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;

	/*
		Check properties of the node on which the player is standing
	*/
	const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
	// Determine if jumping is possible
	m_can_jump = touching_ground && !in_liquid;
	if(itemgroup_get(f.groups, "disable_jump"))
		m_can_jump = false;
	// Jump key pressed while jumping off from a bouncy block
	if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
		m_speed.Y >= -0.5 * BS) {
		float jumpspeed = movement_speed_jump * physics_override_jump;
		if (m_speed.Y > 1) {
			// Reduce boost when speed already is high
			m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
		} else {
			m_speed.Y += jumpspeed;
		}
		setSpeed(m_speed);
		m_can_jump = false;
	}
}
Esempio n. 8
0
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
	*/
	ITextureSource *tsrc = m_gamedef->tsrc();
	IShaderSource *shdrsrc = m_gamedef->getShaderSource();

	bool enable_shaders     = g_settings->getBool("enable_shaders");

	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)
		{
			// 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;
		}

		for(u32 j = 0; j < p.vertices.size(); j++)
		{
			// Note applyContrast second parameter is precalculated sqrt from original
			// values for speed improvement
			video::SColor &vc = p.vertices[j].Color;
			if(p.vertices[j].Normal.Y > 0.5) {
				applyContrast (vc, 1.095445);
			} else if (p.vertices[j].Normal.Y < -0.5) {
				applyContrast (vc, 0.547723);
			} else if (p.vertices[j].Normal.X > 0.5) {
				applyContrast (vc, 0.707107);
			} else if (p.vertices[j].Normal.X < -0.5) {
				applyContrast (vc, 0.707107);
			} else if (p.vertices[j].Normal.Z > 0.5) {
				applyContrast (vc, 0.894427);
			} else if (p.vertices[j].Normal.Z < -0.5) {
				applyContrast (vc, 0.894427);
			}
			if(!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_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) {
			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
	*/

	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();
}
Esempio n. 9
0
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();
}
Esempio n. 10
0
void LocalPlayer::move(f32 dtime, ClientEnvironment *env, f32 pos_max_d,
		std::list<CollisionInfo> *collision_info)
{
	Map *map = &env->getMap();
	INodeDefManager *nodemgr = m_gamedef->ndef();

	v3f position = getPosition();

	v3f old_speed = m_speed;

	// Copy parent position if local player is attached
	if(isAttached)
	{
		setPosition(overridePosition);
		return;
	}

	// Skip collision detection if noclip mode is used
	bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
	bool noclip = m_gamedef->checkLocalPrivilege("noclip") &&
		g_settings->getBool("noclip");
	bool free_move = noclip && fly_allowed && g_settings->getBool("free_move");
	if(free_move)
	{
        position += m_speed * dtime;
		setPosition(position);
		return;
	}

	/*
		Collision detection
	*/
	
	/*
		Check if player is in liquid (the oscillating value)
	*/
	try{
		// If in liquid, the threshold of coming out is at higher y
		if(in_liquid)
		{
			v3s16 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
			in_liquid = nodemgr->get(map->getNode(pp).getContent()).isLiquid();
			liquid_viscosity = nodemgr->get(map->getNode(pp).getContent()).liquid_viscosity;
		}
		// If not in liquid, the threshold of going in is at lower y
		else
		{
			v3s16 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
			in_liquid = nodemgr->get(map->getNode(pp).getContent()).isLiquid();
			liquid_viscosity = nodemgr->get(map->getNode(pp).getContent()).liquid_viscosity;
		}
	}
	catch(InvalidPositionException &e)
	{
		in_liquid = false;
	}

	/*
		Check if player is in liquid (the stable value)
	*/
	try{
		v3s16 pp = floatToInt(position + v3f(0,0,0), BS);
		in_liquid_stable = nodemgr->get(map->getNode(pp).getContent()).isLiquid();
	}
	catch(InvalidPositionException &e)
	{
		in_liquid_stable = false;
	}

	/*
	        Check if player is climbing
	*/

	try {
		v3s16 pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
		v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
		is_climbing = ((nodemgr->get(map->getNode(pp).getContent()).climbable ||
		nodemgr->get(map->getNode(pp2).getContent()).climbable) && !free_move);
	}
	catch(InvalidPositionException &e)
	{
		is_climbing = false;
	}

	/*
		Collision uncertainty radius
		Make it a bit larger than the maximum distance of movement
	*/
	//f32 d = pos_max_d * 1.1;
	// A fairly large value in here makes moving smoother
	f32 d = 0.15*BS;

	// This should always apply, otherwise there are glitches
	assert(d > pos_max_d);

	float player_radius = BS*0.30;
	float player_height = BS*1.55;
	
	// Maximum distance over border for sneaking
	f32 sneak_max = BS*0.4;

	/*
		If sneaking, keep in range from the last walked node and don't
		fall off from it
	*/
	if(control.sneak && m_sneak_node_exists && !(fly_allowed && g_settings->getBool("free_move")) && !in_liquid)
	{
		f32 maxd = 0.5*BS + sneak_max;
		v3f lwn_f = intToFloat(m_sneak_node, BS);
		position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd);
		position.Z = rangelim(position.Z, lwn_f.Z-maxd, lwn_f.Z+maxd);
		
		if(!is_climbing)
		{
			f32 min_y = lwn_f.Y + 0.5*BS;
			if(position.Y < min_y)
			{
				position.Y = min_y;

				if(m_speed.Y < 0)
					m_speed.Y = 0;
			}
		}
	}

	/*
		Calculate player collision box (new and old)
	*/
	core::aabbox3d<f32> playerbox(
		-player_radius,
		0.0,
		-player_radius,
		player_radius,
		player_height,
		player_radius
	);

	float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);

	v3f accel_f = v3f(0,0,0);

	collisionMoveResult result = collisionMoveSimple(env, m_gamedef,
			pos_max_d, playerbox, player_stepheight, dtime,
			position, m_speed, accel_f);

	/*
		If the player's feet touch the topside of any node, this is
		set to true.

		Player is allowed to jump when this is true.
	*/
	bool touching_ground_was = touching_ground;
	touching_ground = result.touching_ground;
    
    //bool standing_on_unloaded = result.standing_on_unloaded;

	/*
		Check the nodes under the player to see from which node the
		player is sneaking from, if any.  If the node from under
		the player has been removed, the player falls.
	*/
	v3s16 current_node = floatToInt(position - v3f(0,BS/2,0), BS);
	if(m_sneak_node_exists &&
	   nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
	   m_old_node_below_type != "air")
	{
		// Old node appears to have been removed; that is,
		// it wasn't air before but now it is
		m_need_to_get_new_sneak_node = false;
		m_sneak_node_exists = false;
	}
	else if(nodemgr->get(map->getNodeNoEx(current_node)).name != "air")
	{
		// We are on something, so make sure to recalculate the sneak
		// node.
		m_need_to_get_new_sneak_node = true;
	}
	if(m_need_to_get_new_sneak_node)
	{
		v3s16 pos_i_bottom = floatToInt(position - v3f(0,BS/2,0), BS);
		v2f player_p2df(position.X, position.Z);
		f32 min_distance_f = 100000.0*BS;
		// If already seeking from some node, compare to it.
		/*if(m_sneak_node_exists)
		{
			v3f sneaknode_pf = intToFloat(m_sneak_node, BS);
			v2f sneaknode_p2df(sneaknode_pf.X, sneaknode_pf.Z);
			f32 d_horiz_f = player_p2df.getDistanceFrom(sneaknode_p2df);
			f32 d_vert_f = fabs(sneaknode_pf.Y + BS*0.5 - position.Y);
			// Ignore if player is not on the same level (likely dropped)
			if(d_vert_f < 0.15*BS)
				min_distance_f = d_horiz_f;
		}*/
		v3s16 new_sneak_node = m_sneak_node;
		for(s16 x=-1; x<=1; x++)
		for(s16 z=-1; z<=1; z++)
		{
			v3s16 p = pos_i_bottom + v3s16(x,0,z);
			v3f pf = intToFloat(p, BS);
			v2f node_p2df(pf.X, pf.Z);
			f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
			f32 max_axis_distance_f = MYMAX(
					fabs(player_p2df.X-node_p2df.X),
					fabs(player_p2df.Y-node_p2df.Y));
					
			if(distance_f > min_distance_f ||
					max_axis_distance_f > 0.5*BS + sneak_max + 0.1*BS)
				continue;

			try{
				// The node to be sneaked on has to be walkable
				if(nodemgr->get(map->getNode(p)).walkable == false)
					continue;
				// And the node above it has to be nonwalkable
				if(nodemgr->get(map->getNode(p+v3s16(0,1,0))).walkable == true)
					continue;
			}
			catch(InvalidPositionException &e)
			{
				continue;
			}

			min_distance_f = distance_f;
			new_sneak_node = p;
		}
		
		bool sneak_node_found = (min_distance_f < 100000.0*BS*0.9);

		m_sneak_node = new_sneak_node;
		m_sneak_node_exists = sneak_node_found;

		/*
			If sneaking, the player's collision box can be in air, so
			this has to be set explicitly
		*/
		if(sneak_node_found && control.sneak)
			touching_ground = true;
	}
	
	/*
		Set new position
	*/
	setPosition(position);
	
	/*
		Report collisions
	*/
	bool bouncy_jump = false;
	// Dont report if flying
	if(collision_info && !(g_settings->getBool("free_move") && fly_allowed))
	{
		for(size_t i=0; i<result.collisions.size(); i++){
			const CollisionInfo &info = result.collisions[i];
			collision_info->push_back(info);
			if(info.new_speed.Y - info.old_speed.Y > 0.1*BS &&
					info.bouncy)
				bouncy_jump = true;
		}
	}

	if(bouncy_jump && control.jump){
		m_speed.Y += movement_speed_jump*BS;
		touching_ground = false;
		MtEvent *e = new SimpleTriggerEvent("PlayerJump");
		m_gamedef->event()->put(e);
	}

	if(!touching_ground_was && touching_ground){
		MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
		m_gamedef->event()->put(e);
	}

	{
		camera_barely_in_ceiling = false;
		v3s16 camera_np = floatToInt(getEyePosition(), BS);
		MapNode n = map->getNodeNoEx(camera_np);
		if(n.getContent() != CONTENT_IGNORE){
			if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
				camera_barely_in_ceiling = true;
			}
		}
	}

	/*
		Update the node last under the player
	*/
	m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
	m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
	
	/*
		Check properties of the node on which the player is standing
	*/
	const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
	// Determine if jumping is possible
	m_can_jump = touching_ground && !in_liquid;
	if(itemgroup_get(f.groups, "disable_jump"))
		m_can_jump = false;
}
/*
	sender_peer_id given to this shall be quaranteed to be a valid peer
*/
void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
{
	DSTACK(__FUNCTION_NAME);

	// Ignore packets that don't even fit a command
	if(datasize < 2)
	{
		m_packetcounter.add(60000);
		return;
	}

	ToClientCommand command = (ToClientCommand)readU16(&data[0]);

	//infostream<<"Client: received command="<<command<<std::endl;
	m_packetcounter.add((u16)command);

	/*
		If this check is removed, be sure to change the queue
		system to know the ids
	*/
	if(sender_peer_id != PEER_ID_SERVER)
	{
		infostream<<"Client::ProcessData(): Discarding data not "
				"coming from server: peer_id="<<sender_peer_id
				<<std::endl;
		return;
	}

	u8 ser_version = m_server_ser_ver;

	//infostream<<"Client received command="<<(int)command<<std::endl;

	if(command == TOCLIENT_INIT)
	{
		if(datasize < 3)
			return;

		u8 deployed = data[2];

		infostream<<"Client: TOCLIENT_INIT received with "
				"deployed="<<((int)deployed&0xff)<<std::endl;

		if(deployed < SER_FMT_VER_LOWEST
				|| deployed > SER_FMT_VER_HIGHEST)
		{
			infostream<<"Client: TOCLIENT_INIT: Server sent "
					<<"unsupported ser_fmt_ver"<<std::endl;
			return;
		}

		m_server_ser_ver = deployed;

		// Get player position
		v3s16 playerpos_s16(0, BS*2+BS*20, 0);
		if(datasize >= 2+1+6)
			playerpos_s16 = readV3S16(&data[2+1]);
		v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);

		{ //envlock
			//JMutexAutoLock envlock(m_env_mutex); //bulk comment-out

			// Set player position
			Player *player = m_env.getLocalPlayer();
			assert(player != NULL);
			player->setPosition(playerpos_f);
		}

		if(datasize >= 2+1+6+8)
		{
			// Get map seed
			m_map_seed = readU64(&data[2+1+6]);
			infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
		}

		// Reply to server
		u32 replysize = 2;
		SharedBuffer<u8> reply(replysize);
		writeU16(&reply[0], TOSERVER_INIT2);
		// Send as reliable
		m_con.Send(PEER_ID_SERVER, 1, reply, true);

		return;
	}

	if(command == TOCLIENT_ACCESS_DENIED)
	{
		// The server didn't like our password. Note, this needs
		// to be processed even if the serialisation format has
		// not been agreed yet, the same as TOCLIENT_INIT.
		m_access_denied = true;
		m_access_denied_reason = L"Unknown";
		if(datasize >= 4)
		{
			std::string datastring((char*)&data[2], datasize-2);
			std::istringstream is(datastring, std::ios_base::binary);
			m_access_denied_reason = deSerializeWideString(is);
		}
		return;
	}

	if(ser_version == SER_FMT_VER_INVALID)
	{
		infostream<<"Client: Server serialization"
				" format invalid or not initialized."
				" Skipping incoming command="<<command<<std::endl;
		return;
	}

	// Just here to avoid putting the two if's together when
	// making some copypasta
	{}

	if(command == TOCLIENT_REMOVENODE)
	{
		if(datasize < 8)
			return;
		v3s16 p;
		p.X = readS16(&data[2]);
		p.Y = readS16(&data[4]);
		p.Z = readS16(&data[6]);

		//TimeTaker t1("TOCLIENT_REMOVENODE");

		// This will clear the cracking animation after digging
		((ClientMap&)m_env.getMap()).clearTempMod(p);

		removeNode(p);
	}
	else if(command == TOCLIENT_ADDNODE)
	{
		if(datasize < 8 + MapNode::serializedLength(ser_version))
			return;

		v3s16 p;
		p.X = readS16(&data[2]);
		p.Y = readS16(&data[4]);
		p.Z = readS16(&data[6]);

		//TimeTaker t1("TOCLIENT_ADDNODE");

		MapNode n;
		n.deSerialize(&data[8], ser_version);

		addNode(p, n);
	}
	else if(command == TOCLIENT_BLOCKDATA)
	{
		// Ignore too small packet
		if(datasize < 8)
			return;

		v3s16 p;
		p.X = readS16(&data[2]);
		p.Y = readS16(&data[4]);
		p.Z = readS16(&data[6]);

		/*infostream<<"Client: Thread: BLOCKDATA for ("
				<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
		/*infostream<<"Client: Thread: BLOCKDATA for ("
				<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/

		std::string datastring((char*)&data[8], datasize-8);
		std::istringstream istr(datastring, std::ios_base::binary);

		MapSector *sector;
		MapBlock *block;

		v2s16 p2d(p.X, p.Z);
		sector = m_env.getMap().emergeSector(p2d);

		assert(sector->getPos() == p2d);

		//TimeTaker timer("MapBlock deSerialize");
		// 0ms

		block = sector->getBlockNoCreateNoEx(p.Y);
		if(block)
		{
			/*
				Update an existing block
			*/
			//infostream<<"Updating"<<std::endl;
			block->deSerialize(istr, ser_version);
		}
		else
		{
			/*
				Create a new block
			*/
			//infostream<<"Creating new"<<std::endl;
			block = new MapBlock(&m_env.getMap(), p);
			block->deSerialize(istr, ser_version);
			sector->insertBlock(block);

			//DEBUG
			/*NodeMod mod;
			mod.type = NODEMOD_CHANGECONTENT;
			mod.param = CONTENT_MESE;
			block->setTempMod(v3s16(8,10,8), mod);
			block->setTempMod(v3s16(8,9,8), mod);
			block->setTempMod(v3s16(8,8,8), mod);
			block->setTempMod(v3s16(8,7,8), mod);
			block->setTempMod(v3s16(8,6,8), mod);*/
		}

#if 0
		/*
			Acknowledge block
		*/
		/*
			[0] u16 command
			[2] u8 count
			[3] v3s16 pos_0
			[3+6] v3s16 pos_1
			...
		*/
		u32 replysize = 2+1+6;
		SharedBuffer<u8> reply(replysize);
		writeU16(&reply[0], TOSERVER_GOTBLOCKS);
		reply[2] = 1;
		writeV3S16(&reply[3], p);
		// Send as reliable
		m_con.Send(PEER_ID_SERVER, 1, reply, true);
#endif

		/*
			Update Mesh of this block and blocks at x-, y- and z-.
			Environment should not be locked as it interlocks with the
			main thread, from which is will want to retrieve textures.
		*/

		//m_env.getClientMap().updateMeshes(block->getPos(), getDayNightRatio());
		/*
			Add it to mesh update queue and set it to be acknowledged after update.
		*/
		//infostream<<"Adding mesh update task for received block"<<std::endl;
		addUpdateMeshTaskWithEdge(p, true);
	}
	else if(command == TOCLIENT_PLAYERPOS)
	{
		infostream<<"Received deprecated TOCLIENT_PLAYERPOS"
				<<std::endl;
		/*u16 our_peer_id;
		{
			//JMutexAutoLock lock(m_con_mutex); //bulk comment-out
			our_peer_id = m_con.GetPeerID();
		}
		// Cancel if we don't have a peer id
		if(our_peer_id == PEER_ID_INEXISTENT){
			infostream<<"TOCLIENT_PLAYERPOS cancelled: "
					"we have no peer id"
					<<std::endl;
			return;
		}*/

		{ //envlock
			//JMutexAutoLock envlock(m_env_mutex); //bulk comment-out

			u32 player_size = 2+12+12+4+4;

			u32 player_count = (datasize-2) / player_size;
			u32 start = 2;
			for(u32 i=0; i<player_count; i++)
			{
				u16 peer_id = readU16(&data[start]);

				Player *player = m_env.getPlayer(peer_id);

				// Skip if player doesn't exist
				if(player == NULL)
				{
					start += player_size;
					continue;
				}

				// Skip if player is local player
				if(player->isLocal())
				{
					start += player_size;
					continue;
				}

				v3s32 ps = readV3S32(&data[start+2]);
				v3s32 ss = readV3S32(&data[start+2+12]);
				s32 pitch_i = readS32(&data[start+2+12+12]);
				s32 yaw_i = readS32(&data[start+2+12+12+4]);
				/*infostream<<"Client: got "
						<<"pitch_i="<<pitch_i
						<<" yaw_i="<<yaw_i<<std::endl;*/
				f32 pitch = (f32)pitch_i / 100.0;
				f32 yaw = (f32)yaw_i / 100.0;
				v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
				v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
				player->setPosition(position);
				player->setSpeed(speed);
				player->setPitch(pitch);
				player->setYaw(yaw);

				/*infostream<<"Client: player "<<peer_id
						<<" pitch="<<pitch
						<<" yaw="<<yaw<<std::endl;*/

				start += player_size;
			}
		} //envlock
	}
	else if(command == TOCLIENT_PLAYERINFO)
	{
		u16 our_peer_id;
		{
			//JMutexAutoLock lock(m_con_mutex); //bulk comment-out
			our_peer_id = m_con.GetPeerID();
		}
		// Cancel if we don't have a peer id
		if(our_peer_id == PEER_ID_INEXISTENT){
			infostream<<"TOCLIENT_PLAYERINFO cancelled: "
					"we have no peer id"
					<<std::endl;
			return;
		}

		//infostream<<"Client: Server reports players:"<<std::endl;

		{ //envlock
			//JMutexAutoLock envlock(m_env_mutex); //bulk comment-out

			u32 item_size = 2+PLAYERNAME_SIZE;
			u32 player_count = (datasize-2) / item_size;
			u32 start = 2;
			// peer_ids
			core::list<u16> players_alive;
			for(u32 i=0; i<player_count; i++)
			{
				// Make sure the name ends in '\0'
				data[start+2+20-1] = 0;

				u16 peer_id = readU16(&data[start]);

				players_alive.push_back(peer_id);

				/*infostream<<"peer_id="<<peer_id
						<<" name="<<((char*)&data[start+2])<<std::endl;*/

				// Don't update the info of the local player
				if(peer_id == our_peer_id)
				{
					start += item_size;
					continue;
				}

				Player *player = m_env.getPlayer(peer_id);

				// Create a player if it doesn't exist
				if(player == NULL)
				{
					player = new RemotePlayer(
							m_device->getSceneManager()->getRootSceneNode(),
							m_device,
							-1);
					player->peer_id = peer_id;
					m_env.addPlayer(player);
					infostream<<"Client: Adding new player "
							<<peer_id<<std::endl;
				}

				player->updateName((char*)&data[start+2]);

				start += item_size;
			}

			/*
				Remove those players from the environment that
				weren't listed by the server.
			*/
			//infostream<<"Removing dead players"<<std::endl;
			core::list<Player*> players = m_env.getPlayers();
			core::list<Player*>::Iterator ip;
			for(ip=players.begin(); ip!=players.end(); ip++)
			{
				// Ingore local player
				if((*ip)->isLocal())
					continue;

				// Warn about a special case
				if((*ip)->peer_id == 0)
				{
					infostream<<"Client: Removing "
							"dead player with id=0"<<std::endl;
				}

				bool is_alive = false;
				core::list<u16>::Iterator i;
				for(i=players_alive.begin(); i!=players_alive.end(); i++)
				{
					if((*ip)->peer_id == *i)
					{
						is_alive = true;
						break;
					}
				}
				/*infostream<<"peer_id="<<((*ip)->peer_id)
						<<" is_alive="<<is_alive<<std::endl;*/
				if(is_alive)
					continue;
				infostream<<"Removing dead player "<<(*ip)->peer_id
						<<std::endl;
				m_env.removePlayer((*ip)->peer_id);
			}
		} //envlock
	}
	else if(command == TOCLIENT_SECTORMETA)
	{
		infostream<<"Client received DEPRECATED TOCLIENT_SECTORMETA"<<std::endl;
#if 0
		/*
			[0] u16 command
			[2] u8 sector count
			[3...] v2s16 pos + sector metadata
		*/
		if(datasize < 3)
			return;

		//infostream<<"Client received TOCLIENT_SECTORMETA"<<std::endl;

		{ //envlock
			//JMutexAutoLock envlock(m_env_mutex); //bulk comment-out

			std::string datastring((char*)&data[2], datasize-2);
			std::istringstream is(datastring, std::ios_base::binary);

			u8 buf[4];

			is.read((char*)buf, 1);
			u16 sector_count = readU8(buf);

			//infostream<<"sector_count="<<sector_count<<std::endl;

			for(u16 i=0; i<sector_count; i++)
			{
				// Read position
				is.read((char*)buf, 4);
				v2s16 pos = readV2S16(buf);
				/*infostream<<"Client: deserializing sector at "
						<<"("<<pos.X<<","<<pos.Y<<")"<<std::endl;*/
				// Create sector
				assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
				((ClientMap&)m_env.getMap()).deSerializeSector(pos, is);
			}
		} //envlock
#endif
	}
	else if(command == TOCLIENT_INVENTORY)
	{
		if(datasize < 3)
			return;

		//TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);

		{ //envlock
			//TimeTaker t2("mutex locking", m_device);
			//JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
			//t2.stop();

			//TimeTaker t3("istringstream init", m_device);
			std::string datastring((char*)&data[2], datasize-2);
			std::istringstream is(datastring, std::ios_base::binary);
			//t3.stop();

			//m_env.printPlayers(infostream);

			//TimeTaker t4("player get", m_device);
			Player *player = m_env.getLocalPlayer();
			assert(player != NULL);
			//t4.stop();

			//TimeTaker t1("inventory.deSerialize()", m_device);
			player->inventory.deSerialize(is);
			//t1.stop();

			m_inventory_updated = true;

			//infostream<<"Client got player inventory:"<<std::endl;
			//player->inventory.print(infostream);
		}
	}
	//DEBUG
	else if(command == TOCLIENT_OBJECTDATA)
	{
		// Strip command word and create a stringstream
		std::string datastring((char*)&data[2], datasize-2);
		std::istringstream is(datastring, std::ios_base::binary);

		u8 buf[12];

		/*
			Read players
		*/

		is.read((char*)buf, 2);
		u16 playercount = readU16(buf);

		for(u16 i=0; i<playercount; i++)
		{
			is.read((char*)buf, 2);
			u16 peer_id = readU16(buf);
			is.read((char*)buf, 12);
			v3s32 p_i = readV3S32(buf);
			is.read((char*)buf, 12);
			v3s32 s_i = readV3S32(buf);
			is.read((char*)buf, 4);
			s32 pitch_i = readS32(buf);
			is.read((char*)buf, 4);
			s32 yaw_i = readS32(buf);

			Player *player = m_env.getPlayer(peer_id);

			// Skip if player doesn't exist
			if(player == NULL)
			{
				continue;
			}

			// Skip if player is local player
			if(player->isLocal())
			{
				continue;
			}

			f32 pitch = (f32)pitch_i / 100.0;
			f32 yaw = (f32)yaw_i / 100.0;
			v3f position((f32)p_i.X/100., (f32)p_i.Y/100., (f32)p_i.Z/100.);
			v3f speed((f32)s_i.X/100., (f32)s_i.Y/100., (f32)s_i.Z/100.);

			player->setPosition(position);
			player->setSpeed(speed);
			player->setPitch(pitch);
			player->setYaw(yaw);
		}

		/*
			Read block objects
			NOTE: Deprecated stuff
		*/

		// Read active block count
		u16 blockcount = readU16(is);
		if(blockcount != 0){
			infostream<<"TOCLIENT_OBJECTDATA: blockcount != 0 "
					"not supported"<<std::endl;
			return;
		}
	}
	else if(command == TOCLIENT_TIME_OF_DAY)
	{
		if(datasize < 4)
			return;

		u16 time_of_day = readU16(&data[2]);
		time_of_day = time_of_day % 24000;
		//infostream<<"Client: time_of_day="<<time_of_day<<std::endl;

		/*
			time_of_day:
			0 = midnight
			12000 = midday
		*/
		{
			m_env.setTimeOfDay(time_of_day);

			u32 dr = m_env.getDayNightRatio();

			infostream<<"Client: time_of_day="<<time_of_day
					<<", dr="<<dr
					<<std::endl;
		}

	}
	else if(command == TOCLIENT_CHAT_MESSAGE)
	{
		/*
			u16 command
			u16 length
			wstring message
		*/
		u8 buf[6];
		std::string datastring((char*)&data[2], datasize-2);
		std::istringstream is(datastring, std::ios_base::binary);

		// Read stuff
		is.read((char*)buf, 2);
		u16 len = readU16(buf);

		std::wstring message;
		for(u16 i=0; i<len; i++)
		{
			is.read((char*)buf, 2);
			message += (wchar_t)readU16(buf);
		}

		/*infostream<<"Client received chat message: "
				<<wide_to_narrow(message)<<std::endl;*/

		m_chat_queue.push_back(message);
	}
	else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
	{
		//if(g_settings->getBool("enable_experimental"))
		{
			/*
				u16 command
				u16 count of removed objects
				for all removed objects {
					u16 id
				}
				u16 count of added objects
				for all added objects {
					u16 id
					u8 type
					u32 initialization data length
					string initialization data
				}
			*/

			char buf[6];
			// Get all data except the command number
			std::string datastring((char*)&data[2], datasize-2);
			// Throw them in an istringstream
			std::istringstream is(datastring, std::ios_base::binary);

			// Read stuff

			// Read removed objects
			is.read(buf, 2);
			u16 removed_count = readU16((u8*)buf);
			for(u16 i=0; i<removed_count; i++)
			{
				is.read(buf, 2);
				u16 id = readU16((u8*)buf);
				// Remove it
				{
					//JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
					m_env.removeActiveObject(id);
				}
			}

			// Read added objects
			is.read(buf, 2);
			u16 added_count = readU16((u8*)buf);
			for(u16 i=0; i<added_count; i++)
			{
				is.read(buf, 2);
				u16 id = readU16((u8*)buf);
				is.read(buf, 1);
				u8 type = readU8((u8*)buf);
				std::string data = deSerializeLongString(is);
				// Add it
				{
					//JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
					m_env.addActiveObject(id, type, data);
				}
			}
		}
	}
	else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
	{
		//if(g_settings->getBool("enable_experimental"))
		{
			/*
				u16 command
				for all objects
				{
					u16 id
					u16 message length
					string message
				}
			*/
			char buf[6];
			// Get all data except the command number
			std::string datastring((char*)&data[2], datasize-2);
			// Throw them in an istringstream
			std::istringstream is(datastring, std::ios_base::binary);

			while(is.eof() == false)
			{
				// Read stuff
				is.read(buf, 2);
				u16 id = readU16((u8*)buf);
				if(is.eof())
					break;
				is.read(buf, 2);
				u16 message_size = readU16((u8*)buf);
				std::string message;
				message.reserve(message_size);
				for(u16 i=0; i<message_size; i++)
				{
					is.read(buf, 1);
					message.append(buf, 1);
				}
				// Pass on to the environment
				{
					//JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
					m_env.processActiveObjectMessage(id, message);
				}
			}
		}
	}
	else if(command == TOCLIENT_HP)
	{
		std::string datastring((char*)&data[2], datasize-2);
		std::istringstream is(datastring, std::ios_base::binary);
		Player *player = m_env.getLocalPlayer();
		assert(player != NULL);
		u8 hp = readU8(is);
		player->hp = hp;
	}
	else if(command == TOCLIENT_MOVE_PLAYER)
	{
		std::string datastring((char*)&data[2], datasize-2);
		std::istringstream is(datastring, std::ios_base::binary);
		Player *player = m_env.getLocalPlayer();
		assert(player != NULL);
		v3f pos = readV3F1000(is);
		f32 pitch = readF1000(is);
		f32 yaw = readF1000(is);
		player->setPosition(pos);
		/*player->setPitch(pitch);
		player->setYaw(yaw);*/

		infostream<<"Client got TOCLIENT_MOVE_PLAYER"
				<<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
				<<" pitch="<<pitch
				<<" yaw="<<yaw
				<<std::endl;

		/*
			Add to ClientEvent queue.
			This has to be sent to the main program because otherwise
			it would just force the pitch and yaw values to whatever
			the camera points to.
		*/
		ClientEvent event;
		event.type = CE_PLAYER_FORCE_MOVE;
		event.player_force_move.pitch = pitch;
		event.player_force_move.yaw = yaw;
		m_client_event_queue.push_back(event);

		// Ignore damage for a few seconds, so that the player doesn't
		// get damage from falling on ground
		m_ignore_damage_timer = 3.0;
	}
	else if(command == TOCLIENT_PLAYERITEM)
	{
		std::string datastring((char*)&data[2], datasize-2);
		std::istringstream is(datastring, std::ios_base::binary);

		u16 count = readU16(is);

		for (u16 i = 0; i < count; ++i) {
			u16 peer_id = readU16(is);
			Player *player = m_env.getPlayer(peer_id);

			if (player == NULL)
			{
				infostream<<"Client: ignoring player item "
					<< deSerializeString(is)
					<< " for non-existing peer id " << peer_id
					<< std::endl;
				continue;
			} else if (player->isLocal()) {
				infostream<<"Client: ignoring player item "
					<< deSerializeString(is)
					<< " for local player" << std::endl;
				continue;
			} else {
				InventoryList *inv = player->inventory.getList("main");
				std::string itemstring(deSerializeString(is));
				if (itemstring.empty()) {
					inv->deleteItem(0);
					infostream
						<<"Client: empty player item for peer "
						<< peer_id << std::endl;
				} else {
					std::istringstream iss(itemstring);
					delete inv->changeItem(0, InventoryItem::deSerialize(iss));
					infostream<<"Client: player item for peer " << peer_id << ": ";
					player->getWieldItem()->serialize(infostream);
					infostream<<std::endl;
				}
			}
		}
	}
	else if(command == TOCLIENT_DEATHSCREEN)
	{
		std::string datastring((char*)&data[2], datasize-2);
		std::istringstream is(datastring, std::ios_base::binary);

		bool set_camera_point_target = readU8(is);
		v3f camera_point_target = readV3F1000(is);

		ClientEvent event;
		event.type = CE_DEATHSCREEN;
		event.deathscreen.set_camera_point_target = set_camera_point_target;
		event.deathscreen.camera_point_target_x = camera_point_target.X;
		event.deathscreen.camera_point_target_y = camera_point_target.Y;
		event.deathscreen.camera_point_target_z = camera_point_target.Z;
		m_client_event_queue.push_back(event);
	}
	else
	{
		infostream<<"Client: Ignoring unknown command "
				<<command<<std::endl;
	}
}
Esempio n. 12
0
PointedThing ClientEnvironment::getPointedThing(
	core::line3d<f32> shootline,
	bool liquids_pointable,
	bool look_for_object)
{
	PointedThing result;

	INodeDefManager *nodedef = m_map->getNodeDefManager();

	core::aabbox3d<s16> maximal_exceed = nodedef->getSelectionBoxIntUnion();
	// The code needs to search these nodes
	core::aabbox3d<s16> search_range(-maximal_exceed.MaxEdge,
		-maximal_exceed.MinEdge);
	// If a node is found, there might be a larger node behind.
	// To find it, we have to go further.
	s16 maximal_overcheck =
		std::max(abs(search_range.MinEdge.X), abs(search_range.MaxEdge.X))
			+ std::max(abs(search_range.MinEdge.Y), abs(search_range.MaxEdge.Y))
			+ std::max(abs(search_range.MinEdge.Z), abs(search_range.MaxEdge.Z));

	const v3f original_vector = shootline.getVector();
	const f32 original_length = original_vector.getLength();

	f32 min_distance = original_length;

	// First try to find an active object
	if (look_for_object) {
		ClientActiveObject *selected_object = getSelectedActiveObject(
			shootline, &result.intersection_point,
			&result.intersection_normal);

		if (selected_object != NULL) {
			min_distance =
				(result.intersection_point - shootline.start).getLength();

			result.type = POINTEDTHING_OBJECT;
			result.object_id = selected_object->getId();
		}
	}

	// Reduce shootline
	if (original_length > 0) {
		shootline.end = shootline.start
			+ shootline.getVector() / original_length * min_distance;
	}

	// Try to find a node that is closer than the selected active
	// object (if it exists).

	voxalgo::VoxelLineIterator iterator(shootline.start / BS,
		shootline.getVector() / BS);
	v3s16 oldnode = iterator.m_current_node_pos;
	// Indicates that a node was found.
	bool is_node_found = false;
	// If a node is found, it is possible that there's a node
	// behind it with a large nodebox, so continue the search.
	u16 node_foundcounter = 0;
	// If a node is found, this is the center of the
	// first nodebox the shootline meets.
	v3f found_boxcenter(0, 0, 0);
	// The untested nodes are in this range.
	core::aabbox3d<s16> new_nodes;
	while (true) {
		// Test the nodes around the current node in search_range.
		new_nodes = search_range;
		new_nodes.MinEdge += iterator.m_current_node_pos;
		new_nodes.MaxEdge += iterator.m_current_node_pos;

		// Only check new nodes
		v3s16 delta = iterator.m_current_node_pos - oldnode;
		if (delta.X > 0)
			new_nodes.MinEdge.X = new_nodes.MaxEdge.X;
		else if (delta.X < 0)
			new_nodes.MaxEdge.X = new_nodes.MinEdge.X;
		else if (delta.Y > 0)
			new_nodes.MinEdge.Y = new_nodes.MaxEdge.Y;
		else if (delta.Y < 0)
			new_nodes.MaxEdge.Y = new_nodes.MinEdge.Y;
		else if (delta.Z > 0)
			new_nodes.MinEdge.Z = new_nodes.MaxEdge.Z;
		else if (delta.Z < 0)
			new_nodes.MaxEdge.Z = new_nodes.MinEdge.Z;

		// For each untested node
		for (s16 x = new_nodes.MinEdge.X; x <= new_nodes.MaxEdge.X; x++) {
			for (s16 y = new_nodes.MinEdge.Y; y <= new_nodes.MaxEdge.Y; y++) {
				for (s16 z = new_nodes.MinEdge.Z; z <= new_nodes.MaxEdge.Z; z++) {
					MapNode n;
					v3s16 np(x, y, z);
					bool is_valid_position;

					n = m_map->getNodeNoEx(np, &is_valid_position);
					if (!(is_valid_position &&
						isPointableNode(n, nodedef, liquids_pointable))) {
						continue;
					}
					std::vector<aabb3f> boxes;
					n.getSelectionBoxes(nodedef, &boxes,
						n.getNeighbors(np, m_map));

					v3f npf = intToFloat(np, BS);
					for (std::vector<aabb3f>::const_iterator i = boxes.begin();
						i != boxes.end(); ++i) {
						aabb3f box = *i;
						box.MinEdge += npf;
						box.MaxEdge += npf;
						v3f intersection_point;
						v3s16 intersection_normal;
						if (!boxLineCollision(box, shootline.start, shootline.getVector(),
							&intersection_point, &intersection_normal)) {
							continue;
						}
						f32 distance = (intersection_point - shootline.start).getLength();
						if (distance >= min_distance) {
							continue;
						}
						result.type = POINTEDTHING_NODE;
						result.node_undersurface = np;
						result.intersection_point = intersection_point;
						result.intersection_normal = intersection_normal;
						found_boxcenter = box.getCenter();
						min_distance = distance;
						is_node_found = true;
					}
				}
			}
		}
		if (is_node_found) {
			node_foundcounter++;
			if (node_foundcounter > maximal_overcheck) {
				break;
			}
		}
		// Next node
		if (iterator.hasNext()) {
			oldnode = iterator.m_current_node_pos;
			iterator.next();
		} else {
			break;
		}
	}

	if (is_node_found) {
		// Set undersurface and abovesurface nodes
		f32 d = 0.002 * BS;
		v3f fake_intersection = result.intersection_point;
		// Move intersection towards its source block.
		if (fake_intersection.X < found_boxcenter.X)
			fake_intersection.X += d;
		else
			fake_intersection.X -= d;

		if (fake_intersection.Y < found_boxcenter.Y)
			fake_intersection.Y += d;
		else
			fake_intersection.Y -= d;

		if (fake_intersection.Z < found_boxcenter.Z)
			fake_intersection.Z += d;
		else
			fake_intersection.Z -= d;

		result.node_real_undersurface = floatToInt(fake_intersection, BS);
		result.node_abovesurface = result.node_real_undersurface
			+ result.intersection_normal;
	}
	return result;
}
Esempio n. 13
0
bool LocalPlayer::updateSneakNode(Map *map, const v3f &position,
		const v3f &sneak_max)
{
	static const v3s16 dir9_center[9] = {
		v3s16( 0, 0,  0),
		v3s16( 1, 0,  0),
		v3s16(-1, 0,  0),
		v3s16( 0, 0,  1),
		v3s16( 0, 0, -1),
		v3s16( 1, 0,  1),
		v3s16(-1, 0,  1),
		v3s16( 1, 0, -1),
		v3s16(-1, 0, -1)
	};

	INodeDefManager *nodemgr = m_client->ndef();
	MapNode node;
	bool is_valid_position;
	bool new_sneak_node_exists = m_sneak_node_exists;

	// We want the top of the sneak node to be below the players feet
	f32 position_y_mod = 0.05 * BS;
	if (m_sneak_node_exists)
		position_y_mod = m_sneak_node_bb_top.MaxEdge.Y - position_y_mod;

	// Get position of current standing node
	const v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS);

	if (current_node != m_sneak_node) {
		new_sneak_node_exists = false;
	} else {
		node = map->getNodeNoEx(current_node, &is_valid_position);
		if (!is_valid_position || !nodemgr->get(node).walkable)
			new_sneak_node_exists = false;
	}

	// Keep old sneak node
	if (new_sneak_node_exists)
		return true;

	// Get new sneak node
	m_sneak_ladder_detected = false;
	f32 min_distance_f = 100000.0 * BS;

	for (const auto &d : dir9_center) {
		const v3s16 p = current_node + d;
		const v3f pf = intToFloat(p, BS);
		const v2f diff(position.X - pf.X, position.Z - pf.Z);
		f32 distance_f = diff.getLength();

		if (distance_f > min_distance_f ||
				fabs(diff.X) > (.5 + .1) * BS + sneak_max.X ||
				fabs(diff.Y) > (.5 + .1) * BS + sneak_max.Z)
			continue;


		// The node to be sneaked on has to be walkable
		node = map->getNodeNoEx(p, &is_valid_position);
		if (!is_valid_position || !nodemgr->get(node).walkable)
			continue;
		// And the node(s) above have to be nonwalkable
		bool ok = true;
		if (!physics_override_sneak_glitch) {
			u16 height = ceilf(
					(m_collisionbox.MaxEdge.Y - m_collisionbox.MinEdge.Y) / BS
			);
			for (u16 y = 1; y <= height; y++) {
				node = map->getNodeNoEx(p + v3s16(0, y, 0), &is_valid_position);
				if (!is_valid_position || nodemgr->get(node).walkable) {
					ok = false;
					break;
				}
			}
		} else {
			// legacy behaviour: check just one node
			node = map->getNodeNoEx(p + v3s16(0, 1, 0), &is_valid_position);
			ok = is_valid_position && !nodemgr->get(node).walkable;
		}
		if (!ok)
			continue;

		min_distance_f = distance_f;
		m_sneak_node = p;
		new_sneak_node_exists = true;
	}

	if (!new_sneak_node_exists)
		return false;

	// Update saved top bounding box of sneak node
	node = map->getNodeNoEx(m_sneak_node);
	std::vector<aabb3f> nodeboxes;
	node.getCollisionBoxes(nodemgr, &nodeboxes);
	m_sneak_node_bb_top = getNodeBoundingBox(nodeboxes);

	if (physics_override_sneak_glitch) {
		// Detect sneak ladder:
		// Node two meters above sneak node must be solid
		node = map->getNodeNoEx(m_sneak_node + v3s16(0, 2, 0),
			&is_valid_position);
		if (is_valid_position && nodemgr->get(node).walkable) {
			// Node three meters above: must be non-solid
			node = map->getNodeNoEx(m_sneak_node + v3s16(0, 3, 0),
				&is_valid_position);
			m_sneak_ladder_detected = is_valid_position &&
				!nodemgr->get(node).walkable;
		}
	}
	return true;
}
Esempio n. 14
0
void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
		std::vector<CollisionInfo> *collision_info)
{
	if (!collision_info || collision_info->empty()) {
		// Node below the feet, update each ClientEnvironment::step()
		m_standing_node = floatToInt(m_position, BS) - v3s16(0, 1, 0);
	}

	// Temporary option for old move code
	if (!physics_override_new_move) {
		old_move(dtime, env, pos_max_d, collision_info);
		return;
	}

	Map *map = &env->getMap();
	INodeDefManager *nodemgr = m_client->ndef();

	v3f position = getPosition();

	// Copy parent position if local player is attached
	if (isAttached) {
		setPosition(overridePosition);
		return;
	}

	// Skip collision detection if noclip mode is used
	bool fly_allowed = m_client->checkLocalPrivilege("fly");
	bool noclip = m_client->checkLocalPrivilege("noclip") &&
		g_settings->getBool("noclip");
	bool free_move = g_settings->getBool("free_move") && fly_allowed;

	if (noclip && free_move) {
		position += m_speed * dtime;
		setPosition(position);
		return;
	}

	/*
		Collision detection
	*/

	bool is_valid_position;
	MapNode node;
	v3s16 pp;

	/*
		Check if player is in liquid (the oscillating value)
	*/

	// If in liquid, the threshold of coming out is at higher y
	if (in_liquid)
	{
		pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
		node = map->getNodeNoEx(pp, &is_valid_position);
		if (is_valid_position) {
			in_liquid = nodemgr->get(node.getContent()).isLiquid();
			liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
		} else {
			in_liquid = false;
		}
	}
	// If not in liquid, the threshold of going in is at lower y
	else
	{
		pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
		node = map->getNodeNoEx(pp, &is_valid_position);
		if (is_valid_position) {
			in_liquid = nodemgr->get(node.getContent()).isLiquid();
			liquid_viscosity = nodemgr->get(node.getContent()).liquid_viscosity;
		} else {
			in_liquid = false;
		}
	}


	/*
		Check if player is in liquid (the stable value)
	*/
	pp = floatToInt(position + v3f(0,0,0), BS);
	node = map->getNodeNoEx(pp, &is_valid_position);
	if (is_valid_position) {
		in_liquid_stable = nodemgr->get(node.getContent()).isLiquid();
	} else {
		in_liquid_stable = false;
	}

	/*
	        Check if player is climbing
	*/


	pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
	v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
	node = map->getNodeNoEx(pp, &is_valid_position);
	bool is_valid_position2;
	MapNode node2 = map->getNodeNoEx(pp2, &is_valid_position2);

	if (!(is_valid_position && is_valid_position2)) {
		is_climbing = false;
	} else {
		is_climbing = (nodemgr->get(node.getContent()).climbable
				|| nodemgr->get(node2.getContent()).climbable) && !free_move;
	}

	/*
		Collision uncertainty radius
		Make it a bit larger than the maximum distance of movement
	*/
	//f32 d = pos_max_d * 1.1;
	// A fairly large value in here makes moving smoother
	f32 d = 0.15*BS;

	// This should always apply, otherwise there are glitches
	sanity_check(d > pos_max_d);

	// Player object property step height is multiplied by BS in
	// /src/script/common/c_content.cpp and /src/content_sao.cpp
	float player_stepheight = (m_cao == nullptr) ? 0.0f :
		(touching_ground ? m_cao->getStepHeight() : (0.2f * BS));

	// TODO this is a problematic hack.
	// Use a better implementation for autojump, or apply a custom stepheight
	// to all players, as this currently creates unintended special movement
	// abilities and advantages for Android players on a server.
#ifdef __ANDROID__
	if (touching_ground)
		player_stepheight += (0.6f * BS);
#endif

	v3f accel_f = v3f(0,0,0);

	collisionMoveResult result = collisionMoveSimple(env, m_client,
		pos_max_d, m_collisionbox, player_stepheight, dtime,
		&position, &m_speed, accel_f);

	bool could_sneak = control.sneak && !free_move && !in_liquid &&
		!is_climbing && physics_override_sneak;

	// Add new collisions to the vector
	if (collision_info && !free_move) {
		v3f diff = intToFloat(m_standing_node, BS) - position;
		f32 distance = diff.getLength();
		// Force update each ClientEnvironment::step()
		bool is_first = collision_info->empty();

		for (const auto &colinfo : result.collisions) {
			collision_info->push_back(colinfo);

			if (colinfo.type != COLLISION_NODE ||
					colinfo.new_speed.Y != 0 ||
					(could_sneak && m_sneak_node_exists))
				continue;

			diff = intToFloat(colinfo.node_p, BS) - position;

			// Find nearest colliding node
			f32 len = diff.getLength();
			if (is_first || len < distance) {
				m_standing_node = colinfo.node_p;
				distance = len;
			}
		}
	}

	/*
		If the player's feet touch the topside of any node, this is
		set to true.

		Player is allowed to jump when this is true.
	*/
	bool touching_ground_was = touching_ground;
	touching_ground = result.touching_ground;
	bool sneak_can_jump = false;

	// Max. distance (X, Z) over border for sneaking determined by collision box
	// * 0.49 to keep the center just barely on the node
	v3f sneak_max = m_collisionbox.getExtent() * 0.49;

	if (m_sneak_ladder_detected) {
		// restore legacy behaviour (this makes the m_speed.Y hack necessary)
		sneak_max = v3f(0.4 * BS, 0, 0.4 * BS);
	}

	/*
		If sneaking, keep on top of last walked node and don't fall off
	*/
	if (could_sneak && m_sneak_node_exists) {
		const v3f sn_f = intToFloat(m_sneak_node, BS);
		const v3f bmin = sn_f + m_sneak_node_bb_top.MinEdge;
		const v3f bmax = sn_f + m_sneak_node_bb_top.MaxEdge;
		const v3f old_pos = position;
		const v3f old_speed = m_speed;
		f32 y_diff = bmax.Y - position.Y;
		m_standing_node = m_sneak_node;

		// (BS * 0.6f) is the basic stepheight while standing on ground
		if (y_diff < BS * 0.6f) {
			// Only center player when they're on the node
			position.X = rangelim(position.X,
				bmin.X - sneak_max.X, bmax.X + sneak_max.X);
			position.Z = rangelim(position.Z,
				bmin.Z - sneak_max.Z, bmax.Z + sneak_max.Z);

			if (position.X != old_pos.X)
				m_speed.X = 0;
			if (position.Z != old_pos.Z)
				m_speed.Z = 0;
		}

		if (y_diff > 0 && m_speed.Y < 0 &&
				(physics_override_sneak_glitch || y_diff < BS * 0.6f)) {
			// Move player to the maximal height when falling or when
			// the ledge is climbed on the next step.
			position.Y = bmax.Y;
			m_speed.Y = 0;
		}

		// Allow jumping on node edges while sneaking
		if (m_speed.Y == 0 || m_sneak_ladder_detected)
			sneak_can_jump = true;

		if (collision_info &&
				m_speed.Y - old_speed.Y > BS) {
			// Collide with sneak node, report fall damage
			CollisionInfo sn_info;
			sn_info.node_p = m_sneak_node;
			sn_info.old_speed = old_speed;
			sn_info.new_speed = m_speed;
			collision_info->push_back(sn_info);
		}
	}

	/*
		Find the next sneak node if necessary
	*/
	bool new_sneak_node_exists = false;

	if (could_sneak)
		new_sneak_node_exists = updateSneakNode(map, position, sneak_max);

	/*
		Set new position but keep sneak node set
	*/
	setPosition(position);
	m_sneak_node_exists = new_sneak_node_exists;

	/*
		Report collisions
	*/

	if(!result.standing_on_object && !touching_ground_was && touching_ground) {
		MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
		m_client->event()->put(e);

		// Set camera impact value to be used for view bobbing
		camera_impact = getSpeed().Y * -1;
	}

	{
		camera_barely_in_ceiling = false;
		v3s16 camera_np = floatToInt(getEyePosition(), BS);
		MapNode n = map->getNodeNoEx(camera_np);
		if(n.getContent() != CONTENT_IGNORE){
			if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){
				camera_barely_in_ceiling = true;
			}
		}
	}

	/*
		Check properties of the node on which the player is standing
	*/
	const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(m_standing_node));
	// Determine if jumping is possible
	m_can_jump = (touching_ground && !in_liquid && !is_climbing)
			|| sneak_can_jump;
	if (itemgroup_get(f.groups, "disable_jump"))
		m_can_jump = false;

	// Jump key pressed while jumping off from a bouncy block
	if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
		m_speed.Y >= -0.5 * BS) {
		float jumpspeed = movement_speed_jump * physics_override_jump;
		if (m_speed.Y > 1) {
			// Reduce boost when speed already is high
			m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
		} else {
			m_speed.Y += jumpspeed;
		}
		setSpeed(m_speed);
		m_can_jump = false;
	}
}