/// <summary> /// Because Octrees are MonoBehaviors, they must be attached to game objects. This static function will deliver an octreeNode, properly attached /// to a game object, of a given derivative type. /// </summary> /// <returns>A game object with an octree node attached.</returns> /// <param name="octreeType">A type derived from the class OctreeNodeAlt, specific to the volume for which this OctreeNodeAlt is to be created.</param> /// <param name="nodeHandle">The node handle to give the OctreeNodeAlt</param> /// <param name="parent">The parent game object of the OctreeNodeAlt</param> public static OctreeNodeAlt CreateOctreeNode(Type octreeType, uint nodeHandle, GameObject parent) { if (octreeType == null) { Debug.LogWarning("OctreeNodeAlt->CreateOctreeNode was passed a null type. Attempting to fail gracefully by creating a non-functional OctreeNodeAlt."); octreeType = typeof(OctreeNodeAlt); } if (!octreeType.IsSubclassOf(typeof(OctreeNodeAlt))) { Debug.LogWarning("OctreeNodeAlt->CreateOctreeNode was passed a type (" + octreeType + ") that does not inherit from OctreeNodeAlt. Attempting to fail gracefully by creating a non-functional OctreeNodeAlt."); octreeType = typeof(OctreeNodeAlt); } //new object GameObject obj = new GameObject(); //Add the component of octreeType, DYNAMICALLY MUAHAHAHAHHA OctreeNodeAlt node = obj.AddComponent(octreeType) as OctreeNodeAlt; //follow the addComponent immediately (I like breaking things into chunks) node.InitializeNode(nodeHandle); node.InitializeObj(null); node.AttachParent(parent); return(node); }
void Start() { if (material == null) { // This shader should be appropriate in most scenarios, and makes a good default. material = Instantiate(Resources.Load("Materials/ColoredCubes", typeof(Material))) as Material; } OctreeNodeAlt toSync = gameObject.GetComponentInChildren <OctreeNodeAlt>() as OctreeNodeAlt; if (toSync != null) { toSync.ForceSync(); } }
/// <summary> /// This function is 'unsafe' in that it does not do any checking to see if i is in bounds (ie: i < 8), or if this.children is null. It WILL avoid creating a node that already exists, and will delete a misplaced node entirely. This function is called as part of SyncNode and typically should not be overwritten. /// </summary> /// <param name="i">The index of the child node computed using uint i = x + y<<1 + z<<2; or a for loop such as SyncNode's, where we can think of x being the inner loop and z the outer. </param> /// <param name="childNodeHandle">Child node handle.</param> protected void SetChild(uint i, uint childNodeHandle) { if (children[i] != null) { if (children[i].nodeHandle == childNodeHandle) { return; } else { //Debug.Log ("Could this be the culprit making the infinite? Trace node Handle|childNodeHandle: " + children[i].nodeHandle + "|" + childNodeHandle); DisposeChild(i); } } children[i] = OctreeNodeAlt.CreateOctreeNode(this.GetType(), childNodeHandle, gameObject); }
/// <summary> /// Performs operations on a new OctreeNodeAlt GameObject, and does not have to be called by the average Editor-User. Typically called in CreateOctreeNode, immediately after InitializeNode. /// It may be overwritten with care (particularly in building the localPosition) /// </summary> /// <param name="parent">The parent game object, typically expected to either be something derived from Cubiquity.Volume OR another Octree Node of an identical type.</param> public virtual void AttachParent(GameObject parent) { //we Should be the child of something, but just in case... (Hey it worries me whether !parent works... null is odd in some languages) if (parent == null) { gameObject.transform.localPosition = lowerCorner; return; } //yay unity-relevant initialization stuff gameObject.layer = parent.layer; gameObject.transform.parent = parent.transform; //this is actually what physically hooks them together. It makes gameObject the child of parent //this might seem odd to place here instead of at the top but the order we add transforms matters. If we change localPosition before the parent //transform we'll get funny errors where the whole map condenses on itself. gameObject.transform.localPosition = lowerCorner; //see if we are the child of a volume, or of another node... OctreeNodeAlt parentNode = parent.GetComponent <OctreeNodeAlt>(); //if there is no parent node... if (parentNode != null) { //Debug.Log ("No Parents"); //Transform the localposition by the parentnode gameObject.transform.localPosition -= parentNode.lowerCorner; //grab the parent node's volume renderer & collider volRend = parentNode.volRend; volColl = parentNode.volColl; } else { //Debug.Log ("Volume renderer and collider successfully passed to root."); //otherwise I really hope this is the child of a Volume! volRend = parent.GetComponent <VolumeRenderer>(); volColl = parent.GetComponent <VolumeCollider>(); } //Debug.Log ("Testing VolRend & VolColl: " + volRend.ToString() + volColl.ToString()); }
/// <summary> /// This function can be used to force the entire mesh to sync on next update, which is useful in situations where a very important top level component has changed dramatically (ie, if volumeRenderer is gone). /// </summary> public void ForceSync() { //see if we can get our parent node OctreeNodeAlt parentNode = gameObject.GetComponentInParent <OctreeNodeAlt>() as OctreeNodeAlt; //if there is no parent node... if (parentNode != null) { //grab the parent node's volume renderer & collider volRend = parentNode.volRend; volColl = parentNode.volColl; } else { //Debug.Log ("Volume renderer and collider successfully passed to root."); //otherwise I really hope this is the child of a Volume! volRend = gameObject.GetComponentInParent <VolumeRenderer>() as VolumeRenderer; volColl = gameObject.GetComponentInParent <VolumeCollider>() as VolumeCollider; } //doing this will force all meshes to be rebuilt. Which will force the octreenode to ask "Do i have a volume renderer to even display this with?" meshLastSyncronised = 0; if (children == null) { return; } //this loop will iterate through all the children for (uint i = 0; i < 8; i++) { if (children[i] != null) { children[i].RelayComponentChanges(); } } }
/// <summary> /// Synchronize this instance. /// </summary> protected override void Synchronize() { //super! base.Synchronize(); //Not sure if this needs to be done every synchronization but okay CogBlockVolumeRenderer volRend = gameObject.GetComponent <CogBlockVolumeRenderer>(); //initialize the volume renderer so that it's normals face in the correct direction if (volRend != null) //I miss actionscript with lazy evaluation so I could string these all together in one big if.. { if (volRend.material != null) { // We compute surface normals using derivative operations in the fragment shader, but for some reason // these are backwards on Linux. We can correct for this in the shader by setting the multiplier below. #if UNITY_STANDALONE_LINUX && !UNITY_EDITOR float normalMultiplier = -1.0f; #else float normalMultiplier = 1.0f; #endif volRend.material.SetFloat("normalMultiplier", normalMultiplier); } } // Check to make sure we have anything to Synchronize if (data == null) { return; } //still checking if (!data.volumeHandle.HasValue) { return; } //update the volume CubiquityDLL.UpdateVolume(data.volumeHandle.Value); //try to synchronize the octrees if (CubiquityDLL.HasRootOctreeNode(data.volumeHandle.Value) == 1) { if (rootOctreeNode == null || rootOctreeNodeGameObject == null) { //Debug.Log("Creating RootOctreeNode from null"); uint rootNodeHandle = CubiquityDLL.GetRootOctreeNode(data.volumeHandle.Value); rootOctreeNode = OctreeNodeAlt.CreateOctreeNode(typeof(CogBlockOctreeNode), rootNodeHandle, gameObject) as CogBlockOctreeNode; rootOctreeNodeGameObject = rootOctreeNode.gameObject; } //Sync the Node and remember SyncNode will return syncs remaining. If some are remaining, the mesh is syncronized. isMeshSyncronized = (rootOctreeNode.SyncNode(maxNodesPerSync) != 0); //Where Sync nodes handles re-informed a bunch of nodes based on mesh changes, RelayComponentChanges assumes that something happened //which every node of the octree needs to know about. By setting this flag = true, we can relay those changes quickly down through the whole tree. //of course since RelayComponentChanges doesn't really 'yield' after a certain number of units are updated, it is important to realize this function is //called sparingly, usually only in edit mode, and should not perform incredibly complex per-update operations. if (RelayComponentChanges) { rootOctreeNode.RelayComponentChanges(); } } }