// intersect_RayTriangle(): intersect a ray with a 3D triangle // Input: a ray R, and a triangle T // Output: *I = intersection point (when it exists) // Return: -1 = triangle is degenerate (a segment or point) // 0 = disjoint (no intersect) // 1 = intersect in unique point I1 // 2 = are in the same plane public static int RayTriangle(Line3D R, Triangle3D T, out Vector3 I) { Vector3 u, v, n; // triangle vectors Vector3 dir, w0, w; // ray vectors float r, a, b; // params to calc ray-plane intersect I = new Vector3(); // get triangle edge vectors and plane normal u = T.PointB - T.PointA; v = T.PointC - T.PointA; //n = u.CrossProduct(v); // cross product Vector3.Cross(ref u, ref v, out n); //if (n == (Vector3D)0) // triangle is degenerate // return -1; // do not deal with this case dir = R.End - R.Start; // ray direction vector w0 = R.Start - T.PointA; a = -dot(n, w0); b = dot(n, dir); if (Math.Abs(b) < SMALL_NUM) { // ray is parallel to triangle plane if (a == 0) // ray lies in triangle plane return 2; else return 0; // ray disjoint from plane } // get intersect point of ray with triangle plane r = a / b; if (r < 0.0) // ray goes away from triangle return 0; // => no intersect // for a segment, also test if (r > 1.0) => no intersect I = R.Start + r * dir; // intersect point of ray and plane // is I inside T? float uu, uv, vv, wu, wv, D; uu = dot(u, u); uv = dot(u, v); vv = dot(v, v); w = I - T.PointA; wu = dot(w, u); wv = dot(w, v); D = uv * uv - uu * vv; // get and test parametric coords float s, t; s = (uv * wv - vv * wu) / D; if (s < 0.0 || s > 1.0) // I is outside T return 0; t = (uv * wu - uu * wv) / D; if (t < 0.0 || (s + t) > 1.0) // I is outside T return 0; return 1; // I is in T }
private void UpdatePicking() { bool left = Mouse[OpenTK.Input.MouseButton.Left];//destruct bool middle = Mouse[OpenTK.Input.MouseButton.Middle];//clone material as active bool right = Mouse[OpenTK.Input.MouseButton.Right];//build if (!leftpressedpicking) { if (mouseleftclick) { leftpressedpicking = true; } else { left = false; } } else { if (mouseleftdeclick) { leftpressedpicking = false; left = false; } } float unit_x = 0; float unit_y = 0; int NEAR = 1; int FOV = 600; float ASPECT = 640f / 480; float near_height = NEAR * (float)(Math.Tan(FOV * Math.PI / 360.0)); Vector3 ray = new Vector3(unit_x * near_height * ASPECT, unit_y * near_height, 1);//, 0); Vector3 ray_start_point = new Vector3(0, 0, 0); if (overheadcamera) { float mx = (float)mouse_current.X / Width - 0.5f; float my = (float)mouse_current.Y / Height - 0.5f; //ray_start_point = new Vector3(mx * 1.4f, -my * 1.1f, 0.0f); ray_start_point = new Vector3(mx * 3f, -my * 2.2f, -1.0f); } //Matrix4 the_modelview; //Read the current modelview matrix into the array the_modelview //GL.GetFloat(GetPName.ModelviewMatrix, out the_modelview); if (m_theModelView.Equals(new Matrix4())) { return; } Matrix4 theModelView = m_theModelView; theModelView.Invert(); //the_modelview = new Matrix4(); ray = Vector3.Transform(ray, theModelView); ray_start_point = Vector3.Transform(ray_start_point, theModelView); var pick = new Line3D(); var raydir = -(ray - ray_start_point); raydir.Normalize(); raydir = Vector3.Multiply(raydir, 100); pick.Start = ray + Vector3.Multiply(raydir, 0.01f); //do not pick behind pick.End = ray + raydir; //pick models selectedmodelid = -1; foreach (var m in game.Models) { Vector3 closestmodelpos = new Vector3(int.MaxValue,int.MaxValue,int.MaxValue); foreach (var t in m.TrianglesForPicking) { Vector3 intersection; if (Collisions.Intersection.RayTriangle(pick, t, out intersection) == 1) { if ((pick.Start - intersection).Length > PICK_DISTANCE) { continue; } if ((pick.Start - intersection).Length < (pick.Start - closestmodelpos).Length) { closestmodelpos = intersection; selectedmodelid = m.Id; } } } } if (selectedmodelid != -1) { pickcubepos = new Vector3(-1, -1, -1); if (mouseleftclick) { game.ModelClick(selectedmodelid); } mouseleftclick = false; leftpressedpicking = false; return; } if (left) { weapon.SetAttack(true, false); } else if (right) { weapon.SetAttack(true, true); } if (iii++ % 2 == 0) { return; } //pick terrain var s = new BlockOctreeSearcher(); s.StartBox = new Box3D(0, 0, 0, NextPowerOfTwo((uint)Math.Max(map.MapSizeX, Math.Max(map.MapSizeY, map.MapSizeZ)))); List<BlockPosSide> pick2 = new List<BlockPosSide>(s.LineIntersection(IsTileEmptyForPhysics, getblockheight, pick)); pick2.Sort((a, b) => { return (a.pos - ray_start_point).Length.CompareTo((b.pos - ray_start_point).Length); }); if (overheadcamera && pick2.Count > 0 && left) { //if not picked any object, and mouse button is pressed, then walk to destination. playerdestination = pick2[0].pos; } bool pickdistanceok = pick2.Count > 0 && (pick2[0].pos - (player.playerposition)).Length <= PICK_DISTANCE; bool playertileempty = IsTileEmptyForPhysics( (int)ToMapPos(player.playerposition).X, (int)ToMapPos(player.playerposition).Y, (int)ToMapPos(player.playerposition).Z); bool playertileemptyclose = IsTileEmptyForPhysicsClose( (int)ToMapPos(player.playerposition).X, (int)ToMapPos(player.playerposition).Y, (int)ToMapPos(player.playerposition).Z); BlockPosSide pick0; if (pick2.Count > 0 && ((pickdistanceok && (playertileempty || (playertileemptyclose)) ) || overheadcamera) ) { pickcubepos = pick2[0].Current(); pickcubepos = new Vector3((int)pickcubepos.X, (int)pickcubepos.Y, (int)pickcubepos.Z); pick0 = pick2[0]; } else { pickcubepos = new Vector3(-1, -1, -1); pick0.pos = new Vector3(-1, -1, -1); pick0.side = TileSide.Front; } if (FreeMouse) { if (pick2.Count > 0) { OnPick(pick0); } return; } if ((DateTime.Now - lastbuild).TotalSeconds >= BuildDelay) { if (left && !fastclicking) { //todo animation fastclicking = false; } if (left || right || middle) { lastbuild = DateTime.Now; } if (pick2.Count > 0) { if (middle) { var newtile = pick0.Current(); if (MapUtil.IsValidPos(map, (int)newtile.X, (int)newtile.Z, (int)newtile.Y)) { int clonesource = map.GetBlock((int)newtile.X, (int)newtile.Z, (int)newtile.Y); clonesource = (int)data.PlayerBuildableMaterialType((int)clonesource); for (int i = 0; i < materialSlots.Length; i++) { if ((int)materialSlots[i] == clonesource) { activematerial = i; goto done; } } materialSlots[activematerial] = clonesource; done: audio.Play(soundclone); } } if (left || right) { BlockPosSide tile = pick0; Console.Write(tile.pos + ":" + Enum.GetName(typeof(TileSide), tile.side)); Vector3 newtile = right ? tile.Translated() : tile.Current(); if (MapUtil.IsValidPos(map, (int)newtile.X, (int)newtile.Z, (int)newtile.Y)) { Console.WriteLine(". newtile:" + newtile + " type: " + map.GetBlock((int)newtile.X, (int)newtile.Z, (int)newtile.Y)); if (pick0.pos != new Vector3(-1, -1, -1)) { audio.Play(left ? sounddestruct : soundbuild); } if (!right) { particleEffectBlockBreak.StartParticleEffect(newtile);//must be before deletion - gets ground type. } if (!MapUtil.IsValidPos(map, (int)newtile.X, (int)newtile.Z, (int)newtile.Y)) { throw new Exception(); } game.OnPick(new Vector3((int)newtile.X, (int)newtile.Z, (int)newtile.Y), new Vector3((int)tile.Current().X, (int)tile.Current().Z, (int)tile.Current().Y), tile.pos, right); //network.SendSetBlock(new Vector3((int)newtile.X, (int)newtile.Z, (int)newtile.Y), // right ? BlockSetMode.Create : BlockSetMode.Destroy, (byte)MaterialSlots[activematerial]); } } } } fastclicking = false; if (!(left || right || middle)) { lastbuild = new DateTime(); fastclicking = true; } }
public static BlockPosSide? CheckLineBoxExact(Line3D line, Box3D box) { if (PointInBox(line.Start, box)) { return new BlockPosSide() { pos = line.Start }; } Vector3 big = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue); Vector3 closest = big; TileSide side = TileSide.Top; foreach (Triangle3DAndSide t in BoxTrianglesAndSides(box.MinEdge, box.MaxEdge)) { Vector3 i; if (RayTriangle(line, t.t, out i) != 0) { if ((line.Start - i).Length < (line.Start - closest).Length) { closest = i; side = t.side; } } } //if (closest == big) { throw new Exception(); } if (closest == big) { return null; } return new BlockPosSide() { pos = closest, side = side }; //if (PointInBox(line.End, box)) { return new TilePosSide() { pos = line.End }; } throw new Exception(); }
/// <summary> /// Warning: randomly returns incorrect hit position (back side of box). /// </summary> /// <param name="box"></param> /// <param name="line"></param> /// <param name="hit"></param> /// <returns></returns> public static bool CheckLineBox(Box3D box, Line3D line, out Vector3 hit) { return CheckLineBox(box.MinEdge, box.MaxEdge, line.Start, line.End, out hit); }
public IEnumerable<BlockPosSide> LineIntersection(IsBlockEmpty isEmpty, GetBlockHeight getBlockHeight, Line3D line) { currentLine = line; currentHit = new Vector3(); foreach (var node in Search(BoxHit)) { Vector3 hit = currentHit; int x = (int)node.MinEdge.X; int y = (int)node.MinEdge.Z; int z = (int)node.MinEdge.Y; if (!isEmpty(x, y, z)) { var node2 = node; node2.MaxEdge.Y = node2.MinEdge.Y + getBlockHeight(x, y, z); var hit2 = Intersection.CheckLineBoxExact(line, node2); if (hit2 != null) { yield return hit2.Value; } } } }
private void UpdatePicking() { bool left = Mouse[OpenTK.Input.MouseButton.Left];//destruct bool middle = Mouse[OpenTK.Input.MouseButton.Middle];//clone material as active bool right = Mouse[OpenTK.Input.MouseButton.Right];//build if (!leftpressedpicking) { if (mouseleftclick) { leftpressedpicking = true; } else { left = false; } } else { if (mouseleftdeclick) { leftpressedpicking = false; left = false; } } if (!left) { currentAttackedBlock = null; } float pick_distance = PICK_DISTANCE; if (cameratype == CameraType.Tpp) { pick_distance = tppcameradistance * 2; } if (cameratype == CameraType.Overhead) { pick_distance = overheadcameradistance; } float unit_x = 0; float unit_y = 0; int NEAR = 1; int FOV = 600; float ASPECT = 640f / 480; float near_height = NEAR * (float)(Math.Tan(FOV * Math.PI / 360.0)); Vector3 ray = new Vector3(unit_x * near_height * ASPECT, unit_y * near_height, 1);//, 0); Vector3 ray_start_point = new Vector3(0, 0, 0); if (overheadcamera) { float mx = (float)mouse_current.X / Width - 0.5f; float my = (float)mouse_current.Y / Height - 0.5f; //ray_start_point = new Vector3(mx * 1.4f, -my * 1.1f, 0.0f); ray_start_point = new Vector3(mx * 3f, -my * 2.2f, -1.0f); } //Matrix4 the_modelview; //Read the current modelview matrix into the array the_modelview //GL.GetFloat(GetPName.ModelviewMatrix, out the_modelview); if (d_The3d.ModelViewMatrix.Equals(new Matrix4())) { return; } Matrix4 theModelView = d_The3d.ModelViewMatrix; theModelView.Invert(); //the_modelview = new Matrix4(); ray = Vector3.Transform(ray, theModelView); ray_start_point = Vector3.Transform(ray_start_point, theModelView); Line3D pick = new Line3D(); Vector3 raydir = -(ray - ray_start_point); raydir.Normalize(); pick.Start = ray + Vector3.Multiply(raydir, 1f); //do not pick behind pick.End = ray + Vector3.Multiply(raydir, pick_distance * 2); //pick models selectedmodelid = -1; foreach (var m in Models) { Vector3 closestmodelpos = new Vector3(int.MaxValue, int.MaxValue, int.MaxValue); foreach (var t in m.TrianglesForPicking) { Vector3 intersection; if (Collisions.Intersection.RayTriangle(pick, t, out intersection) == 1) { if ((pick.Start - intersection).Length > pick_distance) { continue; } if ((pick.Start - intersection).Length < (pick.Start - closestmodelpos).Length) { closestmodelpos = intersection; selectedmodelid = m.Id; } } } } if (selectedmodelid != -1) { pickcubepos = new Vector3(-1, -1, -1); if (mouseleftclick) { ModelClick(selectedmodelid); } mouseleftclick = false; leftpressedpicking = false; return; } if (left) { d_Weapon.SetAttack(true, false); } else if (right) { d_Weapon.SetAttack(true, true); } //if (iii++ % 2 == 0) { //To improve speed, update picking only every second frame. //return; } //pick terrain var s = new BlockOctreeSearcher(); s.StartBox = new Box3D(0, 0, 0, BitTools.NextPowerOfTwo((uint)Math.Max(d_Map.MapSizeX, Math.Max(d_Map.MapSizeY, d_Map.MapSizeZ)))); List<BlockPosSide> pick2 = new List<BlockPosSide>(s.LineIntersection(IsTileEmptyForPhysics, getblockheight, pick)); pick2.Sort((a, b) => { return (a.pos - ray_start_point).Length.CompareTo((b.pos - ray_start_point).Length); }); if (overheadcamera && pick2.Count > 0 && left) { //if not picked any object, and mouse button is pressed, then walk to destination. playerdestination = pick2[0].pos; } bool pickdistanceok = pick2.Count > 0 && (pick2[0].pos - (player.playerposition)).Length <= pick_distance; bool playertileempty = IsTileEmptyForPhysics( (int)ToMapPos(player.playerposition).X, (int)ToMapPos(player.playerposition).Y, (int)ToMapPos(player.playerposition).Z); bool playertileemptyclose = IsTileEmptyForPhysicsClose( (int)ToMapPos(player.playerposition).X, (int)ToMapPos(player.playerposition).Y, (int)ToMapPos(player.playerposition).Z); BlockPosSide pick0; if (pick2.Count > 0 && ((pickdistanceok && (playertileempty || (playertileemptyclose))) || overheadcamera) ) { pickcubepos = pick2[0].Current(); pickcubepos = new Vector3((int)pickcubepos.X, (int)pickcubepos.Y, (int)pickcubepos.Z); pick0 = pick2[0]; } else { pickcubepos = new Vector3(-1, -1, -1); pick0.pos = new Vector3(-1, -1, -1); pick0.side = TileSide.Front; } if (FreeMouse) { if (pick2.Count > 0) { OnPick(pick0); } return; } var ntile = pick0.Current(); if(IsUsableBlock(d_Map.GetBlock((int)ntile.X, (int)ntile.Z, (int)ntile.Y))) { currentAttackedBlock = new Vector3i((int)ntile.X, (int)ntile.Z, (int)ntile.Y); } if ((DateTime.Now - lastbuild).TotalSeconds >= BuildDelay) { if (left && d_Inventory.RightHand[ActiveMaterial] == null) { PacketClientHealth p = new PacketClientHealth { CurrentHealth = (int)(2 + rnd.NextDouble() * 4) }; SendPacket(Serialize(new PacketClient() { PacketId = ClientPacketId.MonsterHit, Health = p })); } if (left && !fastclicking) { //todo animation fastclicking = false; } if (left || right || middle) { lastbuild = DateTime.Now; } if (pick2.Count > 0) { if (middle) { var newtile = pick0.Current(); if (MapUtil.IsValidPos(d_Map, (int)newtile.X, (int)newtile.Z, (int)newtile.Y)) { int clonesource = d_Map.GetBlock((int)newtile.X, (int)newtile.Z, (int)newtile.Y); int clonesource2 = (int)d_Data.WhenPlayerPlacesGetsConvertedTo[(int)clonesource]; //find this block in another right hand. for (int i = 0; i < 10; i++) { if (d_Inventory.RightHand[i] != null && d_Inventory.RightHand[i].ItemClass == ItemClass.Block && (int)d_Inventory.RightHand[i].BlockId == clonesource2) { ActiveMaterial = i; goto done; } } int? freehand = d_InventoryUtil.FreeHand(ActiveMaterial); //find this block in inventory. foreach (var k in d_Inventory.Items) { if (k.Value.ItemClass == ItemClass.Block && k.Value.BlockId == clonesource2) { //free hand if (freehand != null) { d_InventoryController.WearItem( InventoryPosition.MainArea(k.Key.ToPoint()), InventoryPosition.MaterialSelector(freehand.Value)); goto done; } //try to replace current slot if (d_Inventory.RightHand[ActiveMaterial] != null && d_Inventory.RightHand[ActiveMaterial].ItemClass == ItemClass.Block) { d_InventoryController.MoveToInventory( InventoryPosition.MaterialSelector(ActiveMaterial)); d_InventoryController.WearItem( InventoryPosition.MainArea(k.Key.ToPoint()), InventoryPosition.MaterialSelector(ActiveMaterial)); } } } done: d_Audio.Play(d_Data.CloneSound[clonesource][0]); //todo sound cycle } } if (left || right) { BlockPosSide tile = pick0; Console.Write(tile.pos + ":" + Enum.GetName(typeof(TileSide), tile.side)); Vector3 newtile = right ? tile.Translated() : tile.Current(); if (MapUtil.IsValidPos(d_Map, (int)newtile.X, (int)newtile.Z, (int)newtile.Y)) { Console.WriteLine(". newtile:" + newtile + " type: " + d_Map.GetBlock((int)newtile.X, (int)newtile.Z, (int)newtile.Y)); if (pick0.pos != new Vector3(-1, -1, -1)) { int blocktype; if (left) { blocktype = d_Map.GetBlock((int)newtile.X, (int)newtile.Z, (int)newtile.Y); } else { blocktype = materialSlots[ActiveMaterial]; } if (left && blocktype == d_Data.BlockIdAdminium) { goto end; } string[] sound = left ? d_Data.BreakSound[blocktype] : d_Data.BuildSound[blocktype]; if (sound != null && sound.Length > 0) { d_Audio.Play(sound[0]); //todo sound cycle } } //normal attack if (!right) { //attack var pos = new Vector3i((int)newtile.X, (int)newtile.Z, (int)newtile.Y); currentAttackedBlock = new Vector3i(pos.x,pos.y,pos.z); if (!blockhealth.ContainsKey(pos)) { blockhealth[pos] = GetCurrentBlockHealth(pos.x, pos.y, pos.z); } blockhealth[pos] -= WeaponAttackStrength(); float health = GetCurrentBlockHealth(pos.x,pos.y,pos.z); if (health <= 0) { if (currentAttackedBlock != null) { blockhealth.Remove(currentAttackedBlock.Value); } currentAttackedBlock = null; goto broken; } goto end; } if (!right) { particleEffectBlockBreak.StartParticleEffect(newtile);//must be before deletion - gets ground type. } if (!MapUtil.IsValidPos(d_Map, (int)newtile.X, (int)newtile.Z, (int)newtile.Y)) { throw new Exception(); } broken: OnPick(new Vector3((int)newtile.X, (int)newtile.Z, (int)newtile.Y), new Vector3((int)tile.Current().X, (int)tile.Current().Z, (int)tile.Current().Y), tile.pos, right); //network.SendSetBlock(new Vector3((int)newtile.X, (int)newtile.Z, (int)newtile.Y), // right ? BlockSetMode.Create : BlockSetMode.Destroy, (byte)MaterialSlots[activematerial]); } } } } end: fastclicking = false; if (!(left || right || middle)) { lastbuild = new DateTime(); fastclicking = true; } }
//Don't allow to look through walls. private void LimitThirdPersonCameraToWalls(ref Vector3 eye, Vector3 target, ref float curtppcameradistance) { var ray_start_point = target; var raytarget = eye; var pick = new Line3D(); var raydir = (raytarget - ray_start_point); raydir.Normalize(); raydir = Vector3.Multiply(raydir, tppcameradistance + 1); pick.Start = ray_start_point; pick.End = ray_start_point + raydir; //pick terrain var s = new BlockOctreeSearcher(); s.StartBox = new Box3D(0, 0, 0, BitTools.NextPowerOfTwo((uint)Math.Max(d_Map.MapSizeX, Math.Max(d_Map.MapSizeY, d_Map.MapSizeZ)))); List<BlockPosSide> pick2 = new List<BlockPosSide>(s.LineIntersection(IsTileEmptyForPhysics, getblockheight, pick)); pick2.Sort((a, b) => { return (a.pos - ray_start_point).Length.CompareTo((b.pos - ray_start_point).Length); }); if (pick2.Count > 0) { var pickdistance = (pick2[0].pos - target).Length; curtppcameradistance = Math.Min(pickdistance - 1, curtppcameradistance); if (curtppcameradistance < 0.3f) { curtppcameradistance = 0.3f; } } Vector3 cameraDirection = target - eye; raydir.Normalize(); eye = target + Vector3.Multiply(raydir, curtppcameradistance); }