public bool PlaceObject(SimObjPhysics sop, ReceptacleSpawnPoint rsp, bool PlaceStationary, int degreeIncrement, bool AlwaysPlaceUpright) { if (rsp.ParentSimObjPhys == sop) { #if UNITY_EDITOR Debug.Log("Can't place object inside itself!"); #endif return(false); } //remember the original rotation of the sim object if we need to reset it //Quaternion originalRot = sop.transform.rotation; Vector3 originalPos = sop.transform.position; Quaternion originalRot = sop.transform.rotation; //get the bounding box of the sim object we are trying to place BoxCollider oabb = sop.BoundingBox.GetComponent <BoxCollider>(); //zero out rotation and velocity/angular velocity, then match the target receptacle's rotation sop.transform.rotation = rsp.ReceptacleBox.transform.rotation; Rigidbody sopRB = sop.GetComponent <Rigidbody>(); sopRB.velocity = Vector3.zero; sopRB.angularVelocity = Vector3.zero; //set 360 degree increment to only check one angle, set smaller increments to check more angles when trying to place (warning THIS WILL GET SLOWER) int HowManyRotationsToCheck = 360 / degreeIncrement; Plane BoxBottom; float DistanceFromBoxBottomTosop; List <RotationAndDistanceValues> ToCheck = new List <RotationAndDistanceValues>(); //we'll check 8 rotations for now, replace the 45 later if we want to adjust the amount of checks //get rotations and distance values for 360/increment number of rotations around just the Y axis //we want to check all of these first so that the object is prioritized to be placed "upright" for (int i = 0; i < HowManyRotationsToCheck; i++) { oabb.enabled = true; if (i > 0) { sop.transform.Rotate(new Vector3(0, degreeIncrement, 0), Space.Self); //ToCheck[i].rotation = sop.transform.rotation; Vector3 Offset = oabb.ClosestPoint(oabb.transform.TransformPoint(oabb.center) + -rsp.ReceptacleBox.transform.up * 10); BoxBottom = new Plane(rsp.ReceptacleBox.transform.up, Offset); DistanceFromBoxBottomTosop = Math.Abs(BoxBottom.GetDistanceToPoint(sop.transform.position)); ToCheck.Add(new RotationAndDistanceValues(DistanceFromBoxBottomTosop, sop.transform.rotation)); } else { //no rotate change just yet, check the first position Vector3 Offset = oabb.ClosestPoint(oabb.transform.TransformPoint(oabb.center) + -rsp.ReceptacleBox.transform.up * 10); //was using rsp.point BoxBottom = new Plane(rsp.ReceptacleBox.transform.up, Offset); DistanceFromBoxBottomTosop = BoxBottom.GetDistanceToPoint(sop.transform.position); ToCheck.Add(new RotationAndDistanceValues(DistanceFromBoxBottomTosop, sop.transform.rotation)); } oabb.enabled = false; } //continue to check rotations about the X and Z axes if the object doesn't have to be placed upright if (!AlwaysPlaceUpright) { //ok now try if the X and Z local axis are rotated if it'll fit //these values can cause the object to be placed at crazy angles, so we'll check these last for (int i = 0; i < HowManyRotationsToCheck; i++) { oabb.enabled = true; if (i > 0) { sop.transform.Rotate(new Vector3(0, degreeIncrement, 0), Space.Self); Quaternion oldRotation = sop.transform.rotation; //now add more points by rotating the x axis at this current y rotation for (int j = 0; j < HowManyRotationsToCheck; j++) { sop.transform.Rotate(new Vector3(degreeIncrement, 0, 0), Space.Self); Vector3 Offset = oabb.ClosestPoint(oabb.transform.TransformPoint(oabb.center) + -rsp.ReceptacleBox.transform.up * 10); BoxBottom = new Plane(rsp.ReceptacleBox.transform.up, Offset); DistanceFromBoxBottomTosop = Math.Abs(BoxBottom.GetDistanceToPoint(sop.transform.position)); ToCheck.Add(new RotationAndDistanceValues(DistanceFromBoxBottomTosop, sop.transform.rotation)); } sop.transform.rotation = oldRotation; //now add EVEN more points by rotating the z axis at this current y rotation for (int j = 0; j < HowManyRotationsToCheck; j++) { sop.transform.Rotate(new Vector3(0, 0, degreeIncrement), Space.Self); Vector3 Offset = oabb.ClosestPoint(oabb.transform.TransformPoint(oabb.center) + -rsp.ReceptacleBox.transform.up * 10); BoxBottom = new Plane(rsp.ReceptacleBox.transform.up, Offset); DistanceFromBoxBottomTosop = Math.Abs(BoxBottom.GetDistanceToPoint(sop.transform.position)); ToCheck.Add(new RotationAndDistanceValues(DistanceFromBoxBottomTosop, sop.transform.rotation)); } sop.transform.rotation = oldRotation; } oabb.enabled = false; } } foreach (RotationAndDistanceValues quat in ToCheck) { //if spawn area is clear, spawn it and return true that we spawned it if (CheckSpawnArea(sop, rsp.Point + rsp.ParentSimObjPhys.transform.up * (quat.distance + yoffset), quat.rotation, false)) { //translate position of the target sim object to the rsp.Point and offset in local y up sop.transform.position = rsp.Point + rsp.ReceptacleBox.transform.up * (quat.distance + yoffset);//rsp.Point + sop.transform.up * DistanceFromBottomOfBoxToTransform; sop.transform.rotation = quat.rotation; //now to do a check to make sure the sim object is contained within the Receptacle box, and doesn't have //bits of it hanging out //Check the ReceptacleBox's Sim Object component to see what Type it is. Then check to //see if the type is the kind where the Object placed must be completely contained or just the bottom 4 corners contained int HowManyCornersToCheck = 0; if (ReceptacleRestrictions.OnReceptacles.Contains(rsp.ParentSimObjPhys.ObjType)) { //check that only the bottom 4 corners are in bounds HowManyCornersToCheck = 4; } if (ReceptacleRestrictions.InReceptacles.Contains(rsp.ParentSimObjPhys.ObjType)) { //check that all 8 corners are within bounds HowManyCornersToCheck = 8; } if (ReceptacleRestrictions.InReceptaclesThatOnlyCheckBottomFourCorners.Contains(rsp.ParentSimObjPhys.ObjType)) { //only check bottom 4 corners even though the action is PlaceIn HowManyCornersToCheck = 4; } int CornerCount = 0; //Plane rspPlane = new Plane(rsp.Point, rsp.ParentSimObjPhys.transform.up); //now check the corner count for either the 4 lowest corners, or all 8 corners depending on Corner Count //attmpt to sort corners so that first four corners are the corners closest to the spawn point we are checking against SpawnCorners.Sort(delegate(Vector3 p1, Vector3 p2) { //sort by making a plane where rsp.point is, find the four corners closest to that point //return rspPlane.GetDistanceToPoint(p1).CompareTo(rspPlane.GetDistanceToPoint(p2)); //^ this ended up not working because if something is placed at an angle this no longer makes sense... return(Vector3.Distance(p1, rsp.Point).CompareTo(Vector3.Distance(p2, rsp.Point))); // return Vector3.Distance(new Vector3(0, p1.y, 0), new Vector3(0, rsp.Point.y, 0)).CompareTo( // Vector3.Distance(new Vector3(0, p2.y, 0), new Vector3(0, rsp.Point.y, 0))); }); //ok so this is just checking if there are enough corners in the Receptacle Zone to consider it placed correctly. //originally this looped up to i < HowManyCornersToCheck, but if we just check all the corners, regardless of //sort order, it seems to bypass the issue above of how to sort the corners to find the "bottom" 4 corners, so uh // i guess this might just work without fancy sorting to determine the bottom 4 corners... especially since the "bottom corners" starts to lose meaning as objects are rotated for (int i = 0; i < 8; i++) { if (rsp.Script.CheckIfPointIsInsideReceptacleTriggerBox(SpawnCorners[i])) { CornerCount++; } } //if not enough corners are inside the receptacle, abort if (CornerCount < HowManyCornersToCheck) { sop.transform.rotation = originalRot; sop.transform.position = originalPos; return(false); } //one final check, make sure all corners of object are "above" the receptacle box in question, so we //dont spawn stuff half on a table and it falls over foreach (Vector3 v in SpawnCorners) { if (!rsp.Script.CheckIfPointIsAboveReceptacleTriggerBox(v)) { sop.transform.rotation = originalRot; sop.transform.position = originalPos; return(false); } } //set true if we want objects to be stationary when placed. (if placed on uneven surface, object remains stationary) //if false, once placed the object will resolve with physics (if placed on uneven surface object might slide or roll) if (PlaceStationary == true) { //make object being placed kinematic true sop.GetComponent <Rigidbody>().collisionDetectionMode = CollisionDetectionMode.Discrete; sop.GetComponent <Rigidbody>().isKinematic = true; //check if the parent sim object is one that moves like a drawer - and would require this to be parented //if(rsp.ParentSimObjPhys.DoesThisObjectHaveThisSecondaryProperty(SimObjSecondaryProperty.CanOpen)) sop.transform.SetParent(rsp.ParentSimObjPhys.transform); //if this object is a receptacle and it has other objects inside it, drop them all together if (sop.DoesThisObjectHaveThisSecondaryProperty(SimObjSecondaryProperty.Receptacle)) { PhysicsRemoteFPSAgentController agent = GameObject.Find("FPSController").GetComponent <PhysicsRemoteFPSAgentController>(); agent.DropContainedObjectsStationary(sop);//use stationary version so that colliders are turned back on, but kinematics remain true } //if the target receptacle is a pickupable receptacle, set it to kinematic true as will sence we are placing stationary if (rsp.ParentSimObjPhys.PrimaryProperty == SimObjPrimaryProperty.CanPickup) { rsp.ParentSimObjPhys.GetComponent <Rigidbody>().isKinematic = true; } } //place stationary false, let physics drop everything too else { //if not placing stationary, put all objects under Objects game object GameObject topObject = GameObject.Find("Objects"); //parent to the Objects transform sop.transform.SetParent(topObject.transform); Rigidbody rb = sop.GetComponent <Rigidbody>(); rb.isKinematic = false; rb.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic; //if this object is a receptacle and it has other objects inside it, drop them all together if (sop.DoesThisObjectHaveThisSecondaryProperty(SimObjSecondaryProperty.Receptacle)) { PhysicsRemoteFPSAgentController agent = GameObject.Find("FPSController").GetComponent <PhysicsRemoteFPSAgentController>(); agent.DropContainedObjects(sop); } } sop.isInAgentHand = false;//set agent hand flag // #if UNITY_EDITOR // Debug.Log(sop.name + " succesfully spawned in " +rsp.ParentSimObjPhys.name + " at coordinate " + rsp.Point); // #endif return(true); } } //reset rotation if no valid spawns found //oh now we couldn't spawn it, all the spawn areas were not clear sop.transform.rotation = originalRot; sop.transform.position = originalPos; return(false); }
// TODO: constructLayouts actually tries the layouts and updates dictionary with whether layout successful or not public IEnumerator constructLayouts(List <ReceptacleSpawnPoint> receptacleSpawnPoints, InstantiatePrefabTest spawner, GameObject anchor, GameObject target, bool PlaceStationary, int maxcount, int degreeIncrement, bool AlwaysPlaceUpright) { bool anchorSpawned; bool targetSpawned; Quaternion rotation; // radius of anchor Vector3 anchorSize = anchor.GetComponent <SimObjPhysics>().BoundingBox.GetComponent <BoxCollider>().size; float r_a = anchorSize[0] / 2; // radius_anchor // height of anchor float h_a = anchorSize[1]; // height_anchor // radius of target Vector3 targetSize = target.GetComponent <SimObjPhysics>().BoundingBox.GetComponent <BoxCollider>().size; float r_t = targetSize[0] / 2; // radius_target // construct list of distances List <float> distances = new List <float> { 0, r_a + r_t, r_a + 2 * r_t, r_a + 4 * r_t, r_a + 8 * r_t }; // construct list of heights List <float> heights = new List <float> { 0, h_a, 2 * h_a }; // construct list of angles List <int> angles = new List <int> { 0, 45, 90, 135, 180, 225, 270, 315 }; List <ReceptacleSpawnPoint> pointList = new List <ReceptacleSpawnPoint>(1); List <ReceptacleSpawnPoint> targetPointList = new List <ReceptacleSpawnPoint>(1); ReceptacleSpawnPoint firstRSP = receptacleSpawnPoints[0]; pointList.Add(firstRSP); targetPointList.Add(new ReceptacleSpawnPoint(Vector3.zero, firstRSP.ReceptacleBox, firstRSP.Script, firstRSP.ParentSimObjPhys)); foreach (ReceptacleSpawnPoint point in receptacleSpawnPoints) { pointList[0] = point; anchorSpawned = spawner.PlaceObjectReceptacle(pointList, anchor.GetComponent <SimObjPhysics>(), PlaceStationary, maxcount, degreeIncrement, AlwaysPlaceUpright, true); // if spawn successful, try placing target if (anchorSpawned) { foreach (int angle in angles) { foreach (float distance in distances) { // ignoring height for now rotation = Quaternion.AngleAxis(angle, Vector3.up); targetPointList[0].Point = point.Point + rotation * Vector3.left * distance; targetSpawned = spawner.PlaceObjectReceptacle(targetPointList, target.GetComponent <SimObjPhysics>(), PlaceStationary, maxcount, degreeIncrement, AlwaysPlaceUpright, true); if (!targetSpawned) { break; } yield return(null); // put this after break, so frames where target doesn't spawn aren't rendered } } } // if (!anchorSpawned) { // #if UNITY_EDITOR // Debug.Log(anchor.name + " could not be spawned."); // #endif // } } }