aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--OpenSim/Framework/ILandObject.cs47
-rw-r--r--OpenSim/Region/CoreModules/World/Land/LandObject.cs290
2 files changed, 319 insertions, 18 deletions
diff --git a/OpenSim/Framework/ILandObject.cs b/OpenSim/Framework/ILandObject.cs
index db1496c..ef7a9e6 100644
--- a/OpenSim/Framework/ILandObject.cs
+++ b/OpenSim/Framework/ILandObject.cs
@@ -97,20 +97,63 @@ namespace OpenSim.Framework
97 /// <remarks> 97 /// <remarks>
98 /// Land co-ordinates are zero indexed. The inputs are treated as points. So if you want to create a bitmap 98 /// Land co-ordinates are zero indexed. The inputs are treated as points. So if you want to create a bitmap
99 /// that covers an entire 256 x 256m region apart from a strip of land on the east, then you would need to 99 /// that covers an entire 256 x 256m region apart from a strip of land on the east, then you would need to
100 /// specify start_x = 0, start_y = 0, end_x = 252 (or anything up to 255), end_y = 256. 100 /// specify start_x = 0, start_y = 0, end_x = 252 (or anything up to 255), end_y = 255.
101 /// 101 ///
102 /// At the moment, the smallest parcel of land is 4m x 4m, so if the 102 /// At the moment, the smallest parcel of land is 4m x 4m, so if the
103 /// region is 256 x 256m (the SL size), the bitmap returned will start at (0,0) and end at (63,63). 103 /// region is 256 x 256m (the SL size), the bitmap returned will start at (0,0) and end at (63,63).
104 /// The value of the set_value needs to be true to define an active parcel of the given size.
104 /// </remarks> 105 /// </remarks>
105 /// <param name="start_x"></param> 106 /// <param name="start_x"></param>
106 /// <param name="start_y"></param> 107 /// <param name="start_y"></param>
107 /// <param name="end_x"></param> 108 /// <param name="end_x"></param>
108 /// <param name="end_y"></param> 109 /// <param name="end_y"></param>
110 /// <param name="set_value"></param>
109 /// <returns>The bitmap created.</returns> 111 /// <returns>The bitmap created.</returns>
110 bool[,] GetSquareLandBitmap(int start_x, int start_y, int end_x, int end_y); 112 bool[,] GetSquareLandBitmap(int start_x, int start_y, int end_x, int end_y, bool set_value = true);
111 113
112 bool[,] ModifyLandBitmapSquare(bool[,] land_bitmap, int start_x, int start_y, int end_x, int end_y, bool set_value); 114 bool[,] ModifyLandBitmapSquare(bool[,] land_bitmap, int start_x, int start_y, int end_x, int end_y, bool set_value);
115
116 /// <summary>
117 /// Merge two (same size) land bitmaps.
118 /// </summary>
119 /// <param name="bitmap_base"></param>
120 /// <param name="bitmap_add"></param>
121 /// <returns>The modified bitmap.</returns>
113 bool[,] MergeLandBitmaps(bool[,] bitmap_base, bool[,] bitmap_add); 122 bool[,] MergeLandBitmaps(bool[,] bitmap_base, bool[,] bitmap_add);
123
124 /// <summary>
125 /// Remap a land bitmap. Takes the supplied land bitmap and rotates it, crops it and finally offsets it into
126 /// a final land bitmap of the target region size.
127 /// </summary>
128 /// <param name="bitmap_base">The original parcel bitmap</param>
129 /// <param name="rotationDegrees"></param>
130 /// <param name="displacement">&lt;x,y,?&gt;</param>
131 /// <param name="boundingOrigin">&lt;x,y,?&gt;</param>
132 /// <param name="boundingSize">&lt;x,y,?&gt;</param>
133 /// <param name="regionSize">&lt;x,y,?&gt;</param>
134 /// <param name="isEmptyNow">out: This is set if the resultant bitmap is now empty</param>
135 /// <param name="AABBMin">out: parcel.AABBMin &lt;x,y,0&gt</param>
136 /// <param name="AABBMax">out: parcel.AABBMax &lt;x,y,0&gt</param>
137 /// <returns>New parcel bitmap</returns>
138 bool[,] RemapLandBitmap(bool[,] bitmap_base, Vector2 displacement, float rotationDegrees, Vector2 boundingOrigin, Vector2 boundingSize, Vector2 regionSize, out bool isEmptyNow, out Vector3 AABBMin, out Vector3 AABBMax);
139
140 /// <summary>
141 /// Clears any parcel data in bitmap_base where there exists parcel data in bitmap_new. In other words the parcel data
142 /// in bitmap_new takes over the space of the parcel data in bitmap_base.
143 /// </summary>
144 /// <param name="bitmap_base"></param>
145 /// <param name="bitmap_new"></param>
146 /// <param name="isEmptyNow">out: This is set if the resultant bitmap is now empty</param>
147 /// <param name="AABBMin">out: parcel.AABBMin &lt;x,y,0&gt;</param>
148 /// <param name="AABBMax">out: parcel.AABBMax &lt;x,y,0&gt</param>
149 /// <returns>New parcel bitmap</returns>
150 bool[,] RemoveFromLandBitmap(bool[,] bitmap_base, bool[,] bitmap_new, out bool isEmptyNow, out Vector3 AABBMin, out Vector3 AABBMax);
151
152 byte[] ConvertLandBitmapToBytes();
153 bool[,] ConvertBytesToLandBitmap(bool overrideRegionSize = false);
154 bool IsLandBitmapEmpty(bool[,] landBitmap);
155 void DebugLandBitmap(bool[,] landBitmap);
156
114 void SendForceObjectSelect(int local_id, int request_type, List<UUID> returnIDs, IClientAPI remote_client); 157 void SendForceObjectSelect(int local_id, int request_type, List<UUID> returnIDs, IClientAPI remote_client);
115 void SendLandObjectOwners(IClientAPI remote_client); 158 void SendLandObjectOwners(IClientAPI remote_client);
116 void ReturnLandObjects(uint type, UUID[] owners, UUID[] tasks, IClientAPI remote_client); 159 void ReturnLandObjects(uint type, UUID[] owners, UUID[] tasks, IClientAPI remote_client);
diff --git a/OpenSim/Region/CoreModules/World/Land/LandObject.cs b/OpenSim/Region/CoreModules/World/Land/LandObject.cs
index 040c90b..16d26c4 100644
--- a/OpenSim/Region/CoreModules/World/Land/LandObject.cs
+++ b/OpenSim/Region/CoreModules/World/Land/LandObject.cs
@@ -864,17 +864,17 @@ namespace OpenSim.Region.CoreModules.World.Land
864 864
865 public bool[,] BasicFullRegionLandBitmap() 865 public bool[,] BasicFullRegionLandBitmap()
866 { 866 {
867 return GetSquareLandBitmap(0, 0, (int)m_scene.RegionInfo.RegionSizeX, (int) m_scene.RegionInfo.RegionSizeY); 867 return GetSquareLandBitmap(0, 0, (int)m_scene.RegionInfo.RegionSizeX, (int) m_scene.RegionInfo.RegionSizeY, true);
868 } 868 }
869 869
870 public bool[,] GetSquareLandBitmap(int start_x, int start_y, int end_x, int end_y) 870 public bool[,] GetSquareLandBitmap(int start_x, int start_y, int end_x, int end_y, bool set_value = true)
871 { 871 {
872 // Empty bitmap for the whole region 872 // Empty bitmap for the whole region
873 bool[,] tempBitmap = new bool[m_scene.RegionInfo.RegionSizeX / landUnit, m_scene.RegionInfo.RegionSizeY / landUnit]; 873 bool[,] tempBitmap = new bool[m_scene.RegionInfo.RegionSizeX / landUnit, m_scene.RegionInfo.RegionSizeY / landUnit];
874 tempBitmap.Initialize(); 874 tempBitmap.Initialize();
875 875
876 // Fill the bitmap square area specified by state and end 876 // Fill the bitmap square area specified by state and end
877 tempBitmap = ModifyLandBitmapSquare(tempBitmap, start_x, start_y, end_x, end_y, true); 877 tempBitmap = ModifyLandBitmapSquare(tempBitmap, start_x, start_y, end_x, end_y, set_value);
878 // m_log.DebugFormat("{0} GetSquareLandBitmap. tempBitmapSize=<{1},{2}>", 878 // m_log.DebugFormat("{0} GetSquareLandBitmap. tempBitmapSize=<{1},{2}>",
879 // LogHeader, tempBitmap.GetLength(0), tempBitmap.GetLength(1)); 879 // LogHeader, tempBitmap.GetLength(0), tempBitmap.GetLength(1));
880 return tempBitmap; 880 return tempBitmap;
@@ -944,10 +944,224 @@ namespace OpenSim.Region.CoreModules.World.Land
944 } 944 }
945 945
946 /// <summary> 946 /// <summary>
947 /// Remap a land bitmap. Takes the supplied land bitmap and rotates it, crops it and finally offsets it into
948 /// a final land bitmap of the target region size.
949 /// </summary>
950 /// <param name="bitmap_base">The original parcel bitmap</param>
951 /// <param name="rotationDegrees"></param>
952 /// <param name="displacement">&lt;x,y,?&gt;</param>
953 /// <param name="boundingOrigin">&lt;x,y,?&gt;</param>
954 /// <param name="boundingSize">&lt;x,y,?&gt;</param>
955 /// <param name="regionSize">&lt;x,y,?&gt;</param>
956 /// <param name="isEmptyNow">out: This is set if the resultant bitmap is now empty</param>
957 /// <param name="AABBMin">out: parcel.AABBMin &lt;x,y,0&gt</param>
958 /// <param name="AABBMax">out: parcel.AABBMax &lt;x,y,0&gt</param>
959 /// <returns>New parcel bitmap</returns>
960 public bool[,] RemapLandBitmap(bool[,] bitmap_base, Vector2 displacement, float rotationDegrees, Vector2 boundingOrigin, Vector2 boundingSize, Vector2 regionSize, out bool isEmptyNow, out Vector3 AABBMin, out Vector3 AABBMax)
961 {
962 // get the size of the incoming bitmap
963 int baseX = bitmap_base.GetLength(0);
964 int baseY = bitmap_base.GetLength(1);
965
966 // create an intermediate bitmap that is 25% bigger on each side that we can work with to handle rotations
967 int offsetX = baseX / 4; // the original origin will now be at these coordinates so now we can have imaginary negative coordinates ;)
968 int offsetY = baseY / 4;
969 int tmpX = baseX + baseX / 2;
970 int tmpY = baseY + baseY / 2;
971 int centreX = tmpX / 2;
972 int centreY = tmpY / 2;
973 bool[,] bitmap_tmp = new bool[tmpX, tmpY];
974
975 double radianRotation = Math.PI * rotationDegrees / 180f;
976 double cosR = Math.Cos(radianRotation);
977 double sinR = Math.Sin(radianRotation);
978 if (rotationDegrees < 0f) rotationDegrees += 360f; //-90=270 -180=180 -270=90
979
980 // So first we apply the rotation to the incoming bitmap, storing the result in bitmap_tmp
981 // We special case orthogonal rotations for accuracy because even using double precision math, Math.Cos(90 degrees) is never fully 0
982 // and we can never rotate around a centre pixel because the bitmap size is always even
983 int x, y, sx, sy;
984 for (y = 0; y <= tmpY; y++)
985 {
986 for (x = 0; x <= tmpX; x++)
987 {
988 if (rotationDegrees == 0f)
989 {
990 sx = x - offsetX;
991 sy = y - offsetY;
992 }
993 else if (rotationDegrees == 90f)
994 {
995 sx = y - offsetX;
996 sy = tmpY - 1 - x - offsetY;
997 }
998 else if (rotationDegrees == 180f)
999 {
1000 sx = tmpX - 1 - x - offsetX;
1001 sy = tmpY - 1 - y - offsetY;
1002 }
1003 else if (rotationDegrees == 270f)
1004 {
1005 sx = tmpX - 1 - y - offsetX;
1006 sy = x - offsetY;
1007 }
1008 else
1009 {
1010 // arbitary rotation: hmmm should I be using (centreX - 0.5) and (centreY - 0.5) and round cosR and sinR to say only 5 decimal places?
1011 sx = centreX + (int)Math.Round((((double)x - centreX) * cosR) + (((double)y - centreY) * sinR)) - offsetX;
1012 sy = centreY + (int)Math.Round((((double)y - centreY) * cosR) - (((double)x - centreX) * sinR)) - offsetY;
1013 }
1014 if (sx >= 0 && sx < baseX && sy >= 0 && sy < baseY)
1015 {
1016 try
1017 {
1018 if (bitmap_base[sx, sy]) bitmap_tmp[x, y] = true;
1019 }
1020 catch (Exception) //just in case we've still not taken care of every way the arrays might go out of bounds! ;)
1021 {
1022 m_log.DebugFormat("{0} RemapLandBitmap Rotate: Out of Bounds sx={1} sy={2} dx={3} dy={4}", LogHeader, sx, sy, x, y);
1023 }
1024 }
1025 }
1026 }
1027
1028 // We could also incorporate the next steps, bounding-rectangle and displacement in the loop above, but it's simpler to visualise if done separately
1029 // and will also make it much easier when later I want the option for maybe a circular or oval bounding shape too ;).
1030 // So... our output land bitmap must be the size of the current region but rememeber, parcel landbitmaps are landUnit metres (4x4 metres) per point,
1031 // and region sizes, boundaries and displacements are in metres so we need to scale down
1032
1033 int newX = (int)(regionSize.X / landUnit);
1034 int newY = (int)(regionSize.Y / landUnit);
1035 bool[,] bitmap_new = new bool[newX, newY];
1036 // displacement is relative to <0,0> in the destination region and defines where the origin of the data selected by the bounding-rectangle is placed
1037 int dispX = (int)Math.Floor(displacement.X / landUnit);
1038 int dispY = (int)Math.Floor(displacement.Y / landUnit);
1039
1040 // startX/Y and endX/Y are coordinates in bitmap_tmp
1041 int startX = (int)Math.Floor(boundingOrigin.X / landUnit) + offsetX;
1042 if (startX > tmpX) startX = tmpX;
1043 if (startX < 0) startX = 0;
1044 int startY = (int)Math.Floor(boundingOrigin.Y / landUnit) + offsetY;
1045 if (startY > tmpY) startY = tmpY;
1046 if (startY < 0) startY = 0;
1047
1048 int endX = (int)Math.Floor((boundingOrigin.X + boundingSize.X) / landUnit) + offsetX;
1049 if (endX > tmpX) endX = tmpX;
1050 if (endX < 0) endX = 0;
1051 int endY = (int)Math.Floor((boundingOrigin.Y + boundingSize.Y) / landUnit) + offsetY;
1052 if (endY > tmpY) endY = tmpY;
1053 if (endY < 0) endY = 0;
1054
1055 //m_log.DebugFormat("{0} RemapLandBitmap: inSize=<{1},{2}>, disp=<{3},{4}> rot={5}, offset=<{6},{7}>, boundingStart=<{8},{9}>, boundingEnd=<{10},{11}>, cosR={12}, sinR={13}, outSize=<{14},{15}>", LogHeader,
1056 // baseX, baseY, dispX, dispY, radianRotation, offsetX, offsetY, startX, startY, endX, endY, cosR, sinR, newX, newY);
1057
1058 isEmptyNow = true;
1059 int minX = newX;
1060 int minY = newY;
1061 int maxX = 0;
1062 int maxY = 0;
1063
1064 int dx, dy;
1065 for (y = startY; y < endY; y++)
1066 {
1067 for (x = startX; x < endX; x++)
1068 {
1069 dx = x - startX + dispX;
1070 dy = y - startY + dispY;
1071 if (dx >= 0 && dx < newX && dy >= 0 && dy < newY)
1072 {
1073 try
1074 {
1075 if (bitmap_tmp[x, y])
1076 {
1077 bitmap_new[dx, dy] = true;
1078 isEmptyNow = false;
1079 if (dx < minX) minX = dx;
1080 if (dy < minY) minY = dy;
1081 if (dx > maxX) maxX = dx;
1082 if (dy > maxY) maxY = dy;
1083 }
1084 }
1085 catch (Exception) //just in case we've still not taken care of every way the arrays might go out of bounds! ;)
1086 {
1087 m_log.DebugFormat("{0} RemapLandBitmap - Bound & Displace: Out of Bounds sx={1} sy={2} dx={3} dy={4}", LogHeader, x, y, dx, dy);
1088 }
1089 }
1090 }
1091 }
1092 if (isEmptyNow)
1093 {
1094 //m_log.DebugFormat("{0} RemapLandBitmap: Land bitmap is marked as Empty", LogHeader);
1095 minX = 0;
1096 minY = 0;
1097 }
1098
1099 AABBMin = new Vector3(minX * landUnit, minY * landUnit, 0);
1100 AABBMax = new Vector3(maxX * landUnit, maxY * landUnit, 0);
1101 return bitmap_new;
1102 }
1103
1104 /// <summary>
1105 /// Clears any parcel data in bitmap_base where there exists parcel data in bitmap_new. In other words the parcel data
1106 /// in bitmap_new takes over the space of the parcel data in bitmap_base.
1107 /// </summary>
1108 /// <param name="bitmap_base"></param>
1109 /// <param name="bitmap_new"></param>
1110 /// <param name="isEmptyNow">out: This is set if the resultant bitmap is now empty</param>
1111 /// <param name="AABBMin">out: parcel.AABBMin &lt;x,y,0&gt</param>
1112 /// <param name="AABBMax">out: parcel.AABBMax &lt;x,y,0&gt</param>
1113 /// <returns>New parcel bitmap</returns>
1114 public bool[,] RemoveFromLandBitmap(bool[,] bitmap_base, bool[,] bitmap_new, out bool isEmptyNow, out Vector3 AABBMin, out Vector3 AABBMax)
1115 {
1116 // get the size of the incoming bitmaps
1117 int baseX = bitmap_base.GetLength(0);
1118 int baseY = bitmap_base.GetLength(1);
1119 int newX = bitmap_new.GetLength(0);
1120 int newY = bitmap_new.GetLength(1);
1121
1122 if (baseX != newX || baseY != newY)
1123 {
1124 throw new Exception(
1125 String.Format("{0} RemoveFromLandBitmap: Land bitmaps are not the same size! baseX={1} baseY={2} newX={3} newY={4}", LogHeader, baseX, baseY, newX, newY));
1126 }
1127
1128 isEmptyNow = true;
1129 int minX = baseX;
1130 int minY = baseY;
1131 int maxX = 0;
1132 int maxY = 0;
1133
1134 for (int y = 0; y < baseY; y++)
1135 {
1136 for (int x = 0; x < baseX; x++)
1137 {
1138 if (bitmap_new[x, y]) bitmap_base[x, y] = false;
1139 if (bitmap_base[x, y])
1140 {
1141 isEmptyNow = false;
1142 if (x < minX) minX = x;
1143 if (y < minY) minY = y;
1144 if (x > maxX) maxX = x;
1145 if (y > maxY) maxY = y;
1146 }
1147 }
1148 }
1149 if (isEmptyNow)
1150 {
1151 //m_log.DebugFormat("{0} RemoveFromLandBitmap: Land bitmap is marked as Empty", LogHeader);
1152 minX = 0;
1153 minY = 0;
1154 }
1155 AABBMin = new Vector3(minX * landUnit, minY * landUnit, 0);
1156 AABBMax = new Vector3(maxX * landUnit, maxY * landUnit, 0);
1157 return bitmap_base;
1158 }
1159
1160 /// <summary>
947 /// Converts the land bitmap to a packet friendly byte array 1161 /// Converts the land bitmap to a packet friendly byte array
948 /// </summary> 1162 /// </summary>
949 /// <returns></returns> 1163 /// <returns></returns>
950 private byte[] ConvertLandBitmapToBytes() 1164 public byte[] ConvertLandBitmapToBytes()
951 { 1165 {
952 byte[] tempConvertArr = new byte[LandBitmap.GetLength(0) * LandBitmap.GetLength(1) / 8]; 1166 byte[] tempConvertArr = new byte[LandBitmap.GetLength(0) * LandBitmap.GetLength(1) / 8];
953 1167
@@ -994,23 +1208,41 @@ namespace OpenSim.Region.CoreModules.World.Land
994 return tempConvertArr; 1208 return tempConvertArr;
995 } 1209 }
996 1210
997 private bool[,] ConvertBytesToLandBitmap() 1211 public bool[,] ConvertBytesToLandBitmap(bool overrideRegionSize = false)
998 { 1212 {
999 bool[,] tempConvertMap = new bool[m_scene.RegionInfo.RegionSizeX / landUnit, m_scene.RegionInfo.RegionSizeY / landUnit]; 1213 int bitmapLen;
1000 tempConvertMap.Initialize(); 1214 int xLen;
1001 byte tempByte = 0; 1215 bool[,] tempConvertMap;
1002 // Math.Min overcomes an old bug that might have made it into the database. Only use the bytes that fit into convertMap. 1216
1003 int bitmapLen = Math.Min(LandData.Bitmap.Length, tempConvertMap.GetLength(0) * tempConvertMap.GetLength(1) / 8); 1217 if (overrideRegionSize)
1004 int xLen = (int)(m_scene.RegionInfo.RegionSizeX / landUnit);
1005
1006 if (bitmapLen == 512)
1007 { 1218 {
1008 // Legacy bitmap being passed in. Use the legacy region size 1219 // Importing land parcel data from an OAR where the source region is a different size to the dest region requires us
1009 // and only set the lower area of the larger region. 1220 // to make a LandBitmap that's not derived from the current region's size. We use the LandData.Bitmap size in bytes
1010 xLen = (int)(Constants.RegionSize / landUnit); 1221 // to figure out what the OAR's region dimensions are. (Is there a better way to get the src region x and y from the OAR?)
1222 // This method assumes we always will have square regions
1223
1224 bitmapLen = LandData.Bitmap.Length;
1225 xLen = (int)Math.Abs(Math.Sqrt(bitmapLen * 8));
1226 tempConvertMap = new bool[xLen, xLen];
1227 tempConvertMap.Initialize();
1228 }
1229 else
1230 {
1231 tempConvertMap = new bool[m_scene.RegionInfo.RegionSizeX / landUnit, m_scene.RegionInfo.RegionSizeY / landUnit];
1232 tempConvertMap.Initialize();
1233 // Math.Min overcomes an old bug that might have made it into the database. Only use the bytes that fit into convertMap.
1234 bitmapLen = Math.Min(LandData.Bitmap.Length, tempConvertMap.GetLength(0) * tempConvertMap.GetLength(1) / 8);
1235 xLen = (int)(m_scene.RegionInfo.RegionSizeX / landUnit);
1236 if (bitmapLen == 512)
1237 {
1238 // Legacy bitmap being passed in. Use the legacy region size
1239 // and only set the lower area of the larger region.
1240 xLen = (int)(Constants.RegionSize / landUnit);
1241 }
1011 } 1242 }
1012 // m_log.DebugFormat("{0} ConvertBytesToLandBitmap: bitmapLen={1}, xLen={2}", LogHeader, bitmapLen, xLen); 1243 // m_log.DebugFormat("{0} ConvertBytesToLandBitmap: bitmapLen={1}, xLen={2}", LogHeader, bitmapLen, xLen);
1013 1244
1245 byte tempByte;
1014 int x = 0, y = 0; 1246 int x = 0, y = 0;
1015 for (int i = 0; i < bitmapLen; i++) 1247 for (int i = 0; i < bitmapLen; i++)
1016 { 1248 {
@@ -1038,6 +1270,32 @@ namespace OpenSim.Region.CoreModules.World.Land
1038 return tempConvertMap; 1270 return tempConvertMap;
1039 } 1271 }
1040 1272
1273 public bool IsLandBitmapEmpty(bool[,] landBitmap)
1274 {
1275 for (int y = 0; y < landBitmap.GetLength(1); y++)
1276 {
1277 for (int x = 0; x < landBitmap.GetLength(0); x++)
1278 {
1279 if (landBitmap[x, y]) return false;
1280 }
1281 }
1282 return true;
1283 }
1284
1285 public void DebugLandBitmap(bool[,] landBitmap)
1286 {
1287 m_log.InfoFormat("{0}: Map Key: #=claimed land .=unclaimed land.", LogHeader);
1288 for (int y = landBitmap.GetLength(1) - 1; y >= 0; y--)
1289 {
1290 string row = "";
1291 for (int x = 0; x < landBitmap.GetLength(0); x++)
1292 {
1293 row += landBitmap[x, y] ? "#" : ".";
1294 }
1295 m_log.InfoFormat("{0}: {1}", LogHeader, row);
1296 }
1297 }
1298
1041 #endregion 1299 #endregion
1042 1300
1043 #region Object Select and Object Owner Listing 1301 #region Object Select and Object Owner Listing