/// <summary>
        /// Assign a single material value.  Based on the values passed we'll either set (or clear) the materials for a SOP.
        /// </summary>
        /// <param name="sop">The SOP being affected.</param>
        /// <param name="face">The face to assign, or -1 if the default texture is being set.</param>
        /// <param name="id">The ID assigned to this material.  Setting a Zero UUID clears it.</param>
        /// <param name="material">If not null, the material to set.  Otherwise we are clearing.</param>
        private void AssignSingleMaterial(SceneObjectPart sop, int face, OSDMap matData)
        {
            UUID id = UUID.Zero;

            // If there is a material being set, see if we've seen it before.
            // If not we'll add it to the Shape RenderMaterials and the region cache
            if (matData != null)
            {
                RenderMaterial material = RenderMaterial.FromOSD(matData);
                id = sop.Shape.RenderMaterials.AddMaterial(material);
                m_scene.EventManager.TriggerRenderMaterialAddedToPrim(sop, id, material);

                m_log.DebugFormat("[RenderMaterials]: SOP {0}, Face {1}, Adding RenderMaterial {2}",
                                  sop.LocalId, face, material.ToString());
            }

            // If the new material is replacing one lets record it so we can clean up
            UUID oldMaterialID = UUID.Zero;

            /// Get a copy of the texture entry so we can make changes.
            var te = new Primitive.TextureEntry(sop.Shape.TextureEntry, 0, sop.Shape.TextureEntry.Length);

            // Set the Material ID in the TextureEntry. If face is ALL_SIDES then
            // set the default entry, otherwise fetch the face and set it there.
            if (face < 0)
            {
                oldMaterialID = te.DefaultTexture.MaterialID;
                te.DefaultTexture.MaterialID = id;
            }
            else
            {
                var faceEntry = te.CreateFace((uint)face);
                oldMaterialID        = faceEntry.MaterialID;
                faceEntry.MaterialID = id;
            }

            // Update the texture entry which will force an update to connected clients
            sop.UpdateTexture(te);

            // If the material has changed and it wasn't previously Zero
            // Deallocate the old value if its not in use and signal the change
            if ((oldMaterialID != id) &&
                (oldMaterialID != UUID.Zero))
            {
                var currentMaterialIDs = sop.Shape.GetMaterialIDs();
                if (currentMaterialIDs.Contains(oldMaterialID) == false)
                {
                    if (sop.Shape.RenderMaterials.ContainsMaterial(oldMaterialID) == true)
                    {
                        sop.Shape.RenderMaterials.RemoveMaterial(oldMaterialID);
                    }

                    m_scene.EventManager.TriggerRenderMaterialRemovedFromPrim(sop, oldMaterialID);
                }
            }
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Assign a single material value.  Based on the values passed we'll either set (or clear) the materials for a SOP.
        /// </summary>
        /// <param name="sop">The SOP being affected.</param>
        /// <param name="face">The face to assign, or -1 if the default texture is being set.</param>
        /// <param name="id">The ID assigned to this material.  Setting a Zero UUID clears it.</param>
        /// <param name="material">If not null, the material to set.  Otherwise we are clearing.</param>
        private void AssignSingleMaterial(SceneObjectPart sop, int face, OSDMap matData)
        {
            /// Get a copy of the texture entry so we can make changes.
            var te = new Primitive.TextureEntry(sop.Shape.TextureEntry, 0, sop.Shape.TextureEntry.Length);

            if (te == null)
            {
                m_log.Warn("[RenderMaterials]: null TextureEntry for localId: " + sop.LocalId.ToString());
                return;
            }

            lock (m_knownMaterials)
            {
                UUID id = UUID.Zero;

                // Record what we currently have
                var currentMaterialIDs = getMaterialIDsFromTextureEntry(te);

                // If there is a material being set see if we've seen it before.
                // If not we'll add it to the region cache
                if (matData != null)
                {
                    RenderMaterial material = RenderMaterial.FromOSD(matData);
                    id = sop.Shape.RenderMaterials.AddMaterial(material);

                    if (m_knownMaterials.ContainsKey(id))
                    {
                        material = m_knownMaterials[id].material;
                        var entry = m_knownMaterials[id];
                        if (entry.partIds.Contains(sop.LocalId) == false)
                        {
                            entry.partIds.Add(sop.LocalId);
                        }
                    }
                    else
                    {
                        m_knownMaterials[id] = new RenderMaterialEntry(material, sop.LocalId);
                    }

                    m_log.DebugFormat("[RenderMaterials]: SOP {0}, Face {1}, Adding RenderMaterial {2}",
                                      sop.LocalId, face, material.ToString());
                }

                // Set the Material ID in the sop texture entry. If there's a face defined
                // use that else use the Default entry.  ID can either be a material ID
                // we set from the lookup above or Zero when we are clearing a material.
                if (face < 0)
                {
                    te.DefaultTexture.MaterialID = id;
                }
                else
                {
                    var faceEntry = te.CreateFace((uint)face);
                    faceEntry.MaterialID = id;
                }

                // Get the updated list of Materials from the TextureEntry.  We will
                // Use that to rebuild the RenderMaterials for the part. We'll also get a list
                // of entries that have been removed based on what we fetched initially so we
                // can clean that up in our cache.
                var newMaterialIDs = getMaterialIDsFromTextureEntry(te);

                // If there was an update and the material id has changed, clean up the old value.
                // Have to be careful here. It might still be in use in another slot.  So we build
                // a list of keys and walk the texture entries subtracting keys in use.  Whatever
                // is left are candidates to clean up.
                foreach (var entry in newMaterialIDs)
                {
                    if (currentMaterialIDs.Contains(entry))
                    {
                        currentMaterialIDs.Remove(entry);
                    }

                    if (sop.Shape.RenderMaterials.ContainsMaterial(entry) == false)
                    {
                        m_log.WarnFormat("[RenderMaterials]: SOP {0}, Face {1}, RenderMaterials {2} should be present but isn't!",
                                         sop.LocalId, face, entry.ToString());
                    }
                }

                // currentMaterialsIDs contains orphans if any.  Remove them from the sop.Shape and the cache (if needed)
                foreach (var entry in currentMaterialIDs)
                {
                    if (sop.Shape.RenderMaterials.ContainsMaterial(entry) == true)
                    {
                        m_log.DebugFormat("[RenderMaterials]: SOP {0}, Face {1}, Removing unused RenderMaterials {2} from shape.",
                                          sop.LocalId, face, entry.ToString());
                        sop.Shape.RenderMaterials.RemoveMaterial(entry);
                    }
                    else
                    {
                        m_log.WarnFormat("[RenderMaterials]: SOP {0}, Face {1}, ORPHANED RenderMaterials {2} should be present but isn't!",
                                         sop.LocalId, face, entry.ToString());
                    }

                    if (m_knownMaterials.ContainsKey(entry))
                    {
                        m_knownMaterials[entry].partIds.Remove(sop.LocalId);
                        if (m_knownMaterials[entry].partIds.Count <= 0)
                        {
                            m_log.DebugFormat("[RenderMaterials]: Removing unused RenderMaterials {0} from region cache.", entry.ToString());
                            m_knownMaterials.Remove(entry);
                        }
                    }
                }
            }

            // Update the texture entry which will force an update to connected clients
            sop.UpdateTextureEntry(te.GetBytes());
        }