public bool CustomPlaceObj()

in IndoorSceneSynthesis/ConstraintStochasticIndoorSceneGeneration/Custom/CustomScripts/CObjectPlacerTool.cs [616:836]


    public bool CustomPlaceObj(SimObjPhysics sop, CustomReceptacleSpawnPoint crsp, bool PlaceStationary, int degreeIncrement, bool alwaysPlaceUpright) {
        if (crsp.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>();
        oabb.enabled = true;

        //zero out rotation and velocity/angular velocity, then match the target receptacle's rotation
        //?????????
        sop.transform.rotation = crsp.ReceptacleBox.transform.rotation;
        Rigidbody sopRB = sop.GetComponent<Rigidbody>();
        sopRB.velocity = Vector3.zero;
        sopRB.angularVelocity = Vector3.zero;

        Plane BoxBottom;
        float DistanceFromBoxBottomTosop;
        InstantiatePrefabTest.RotationAndDistanceValues quat;

        //Vector3 Offset = oabb.ClosestPoint(oabb.transform.TransformPoint(oabb.center) + -crsp.ReceptacleBox.transform.up * 5); //was using rsp.point
        Vector3 target_location = oabb.transform.TransformPoint(oabb.center) + -crsp.ReceptacleBox.transform.up * 5;
        Vector3 Offset = oabb.ClosestPoint(target_location);

        //Debug.Log(oabb.center + " box: " + oabb.bounds.extents.ToString() + " oabb 2 global: " + oabb.transform.TransformPoint(oabb.center) + "offset: " + Offset.ToString());

        BoxBottom = new Plane(crsp.ReceptacleBox.transform.up, Offset);
        DistanceFromBoxBottomTosop = BoxBottom.GetDistanceToPoint(sop.transform.position);

        //quat = new InstantiatePrefabTest.RotationAndDistanceValues(DistanceFromBoxBottomTosop,
        //    Quaternion.Euler(0, -crsp.rotationRad * Mathf.Rad2Deg + 90, 0));

        quat = new InstantiatePrefabTest.RotationAndDistanceValues(DistanceFromBoxBottomTosop,
            Quaternion.Euler(0, crsp.rotationRad * Mathf.Rad2Deg, 0));

        Vector3 newPosition = crsp.Point + crsp.ParentSimObjPhys.transform.up * (quat.distance + this.yoffset);
        //Debug.Log("spawner: " + spawner.gameObject.name + " " + crsp.Point.ToString());
        //Debug.Log("quat: " + quat.distance + " " + quat.rotation + " -crsp.rotateDegree " + -crsp.rotateDegree);

        //if spawn area is clear, spawn it and return true that we spawned it
        //bool noCollison = spawner.CheckSpawnArea(sop, newPosition, quat.rotation, false);

        //track colliders
        List<Collider> colsToDisable = new List<Collider>();
        foreach (Collider g in sop.MyColliders) {
            //only track this collider if it's enabled by default
            //some objects, like houseplants, might have colliders in their simObj.MyColliders that are disabled
            if (g.enabled) {
                colsToDisable.Add(g);
            }
        }

        //GameObject parentObject = crsp.ParentSimObjPhys.gameObject;
        //foreach (Collider c in parentObject.GetComponentsInChildren<Collider>()) {
        //    colsToDisable.Add(c);
        //}


        //disable collision before moving to check the spawn area
        foreach (Collider c in colsToDisable) {
            c.enabled = false;
        }


        //move it into place so the bouding box is in the right spot to generate the overlap box later
        sop.transform.position = newPosition;
        sop.transform.rotation = quat.rotation;

        //now let's get the BoundingBox of the simObj as reference cause we need it to create the overlapbox
        GameObject bb = sop.BoundingBox.transform.gameObject;
        //Debug.Log("sop: " + sop.Type + " layer: " + bb.layer);
        //bb.layer = 8;
        BoxCollider bbcol = bb.GetComponent<BoxCollider>();
        Vector3 bbCenter = bbcol.center;
        Vector3 bbCenterTransformPoint = bb.transform.TransformPoint(bbCenter);

        //move sim object back to it's original spot back so the overlap box doesn't hit it
        sop.transform.position = originalPos;
        sop.transform.rotation = originalRot;


        //re-enable the collision after returning in place
        foreach (Collider c in colsToDisable) {
            c.enabled = true;
        }

        //spawn overlap box
        Collider[] hitColliders = Physics.OverlapBox(
            bbCenterTransformPoint,
            bbcol.size / 2.0f,
            quat.rotation,
            1 << 8 | 1 << 10, //simObjVisible, agent
            QueryTriggerInteraction.Collide
        );

        bool noCollison = hitColliders.Length == 0;

        //Debug.Log("spawner: " + spawner.gameObject.name + " " + crsp.Point.ToString());
        //Debug.Log("quat: " + quat.distance + " " + quat.rotation + " -crsp.rotateDegree " + -crsp.rotateDegree);

        //if spawn area is clear, spawn it and return true that we spawned it
        //bool noCollison = spawner.CheckSpawnArea(sop, crsp.Point + crsp.ParentSimObjPhys.transform.up * (quat.distance + this.yoffset), quat.rotation, false);

        //oabb.enabled = false;
        //Debug.Log(sop.Type + " no collision??????? " + noCollison + " pos " + bbCenterTransformPoint + " size " + bbcol.size / 2.0f);
        if (noCollison) {

            //translate position of the target sim object to the rsp.Point and offset in local y up
            sop.transform.position = crsp.Point + crsp.ReceptacleBox.transform.up * (quat.distance + yoffset);//rsp.Point + sop.transform.up * DistanceFromBottomOfBoxToTransform;
            sop.transform.rotation = quat.rotation;
            //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(crsp.ParentSimObjPhys.ObjType)) {
                //check that only the bottom 4 corners are in bounds
                HowManyCornersToCheck = 4;
            }

            if (ReceptacleRestrictions.InReceptacles.Contains(crsp.ParentSimObjPhys.ObjType)) {
                //check that all 8 corners are within bounds
                HowManyCornersToCheck = 8;
            }

            if (ReceptacleRestrictions.InReceptaclesThatOnlyCheckBottomFourCorners.Contains(crsp.ParentSimObjPhys.ObjType)) {
                //only check bottom 4 corners even though the action is PlaceIn
                HowManyCornersToCheck = 4;
            }

            //Debug.Log("HowManyCornersToCheck: " + HowManyCornersToCheck);

            //Special cases
            if (crsp.ParentSimObjPhys.ObjType == SimObjType.StoveBurner) {
                HowManyCornersToCheck = 0;
            }

            int CornerCount = 0;

            List<Vector3> spawnCorners = GetSimObjCorners(sop);

            spawnCorners.Sort(delegate (Vector3 p1, Vector3 p2) {
                return Vector3.Distance(p1, crsp.Point).CompareTo(Vector3.Distance(p2, crsp.Point));
            });

            for (int i = 0; i < 8; i++) {
                if (crsp.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;
            }

            //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(crsp.ParentSimObjPhys.transform);

                //GameObject topObject = GameObject.Find("Objects");
                //parent to the Objects transform
                //sop.transform.SetParent(topObject.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 (crsp.ParentSimObjPhys.PrimaryProperty == SimObjPrimaryProperty.CanPickup) {
                    crsp.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(target: sop, reparentContainedObjects: true, forceKinematic: false);
                }
            }
            sop.isInAgentHand = false;//set agent hand flag

            objectPool.generatedObjects.Add(sop.gameObject);


            // #if UNITY_EDITOR
            // Debug.Log(sop.name + " succesfully spawned in " +rsp.ParentSimObjPhys.name + " at coordinate " + rsp.Point);
            // #endif

            return true;
        }

        return false;
    }