package NET.worlds.scape; import NET.worlds.console.Main; import NET.worlds.core.Std; import NET.worlds.network.Galaxy; import NET.worlds.network.NetworkRoom; import java.awt.Color; import java.io.IOException; import java.util.Enumeration; import java.util.Vector; public class Room extends WObject implements TeleportStatus { public WObject highlightTarget; private int sceneID; public Point3 defaultPosition = new Point3(100.0F, 100.0F, 120.0F); public Point3 defaultOrientationAxis = new Point3(0.0F, 0.0F, -1.0F); public float defaultOrientation = 0.0F; String teleportChain = "home:avatargallery/avatar.world#AVATARgallery@65.0,145.0,150.0,89.0"; int roomLoadTime = 0; int teleportInterval = -1; boolean teleported = false; private RoomEnvironment environment; private Vector outgoingPortals = new Vector(); private Vector aFilters = new Vector(); private Point3 lightPosition = new Point3(-1.0F, 1.0F, -1.0F); private Color lightColor = new Color(255, 255, 255); private int lightid = 0; private int lightid2 = 0; private int renderStamp; private static int timeoutAge = 15000; private Vector frameHandlers = new Vector(); private Vector frameHandlerWObjects = new Vector(); private Color skyColor; private Color groundColor; private Vector prerenderHandlers = new Vector(); private Vector postrenderHandlers = new Vector(); private static int isRendering = 4; private static int isVIPOnly = 262144; private static int isVIPViewOnly = 524288; private NetworkRoom _netRoom; private static String[] skyOptions = new String[]{"Blue Sky"}; private static String[] groundOptions = new String[]{"Green Ground"}; private boolean allowTeleport = true; private static Object classCookie = new Object(); private RoomEnvironment infiniteBackground; static { nativeInit(); } public Room(World world, String name) { this.setName(name); this.environment = new RoomEnvironment(); this.infiniteBackground = new RoomEnvironment(); super.add(this.environment); super.add(this.infiniteBackground); if (world != null) { world.addRoom(this); } } public Room() { } public static native void nativeInit(); @Override public void getChildren(DeepEnumeration d) { if (this.infiniteBackground != null) { d.addChildElement(this.infiniteBackground); } if (this.environment != null) { d.addChildElement(this.environment); } super.getChildren(d); } @Override public void setName(String newName) { String oldName = this.getName(); super.setName(newName); World world = this.getWorld(); if (world != null) { world.renameRoom(oldName, this.getName(), this); } if (!oldName.equals(newName)) { synchronized (this) { if (this._netRoom != null) { this._netRoom.setName(newName); } } } } synchronized void register() { assert this._netRoom == null; this._netRoom = new NetworkRoom(this); } @Override public void detach() { if (this.hasClump()) { System.out.println("Detaching clumped room " + this.getName()); this.markVoid(); } assert this._netRoom != null; boolean subscribed = Pilot.getActive().removeSubscribedRoom(this); if (subscribed) { this._netRoom.unsubscribe(); } synchronized (this) { this._netRoom.detach(); this._netRoom = null; } this.getWorld().removeRoom(this); super.detach(); } @Override public void discard() { this.environment.discard(); this.infiniteBackground.discard(); super.discard(); this.frameHandlers.removeAllElements(); this.frameHandlerWObjects.removeAllElements(); this.outgoingPortals.removeAllElements(); this.aFilters.removeAllElements(); this.prerenderHandlers.removeAllElements(); this.postrenderHandlers.removeAllElements(); } int getSceneID() { return this.sceneID; } public Point3Temp getDefaultPosition() { return Point3Temp.make(this.defaultPosition); } public Point3Temp getDefaultOrientationAxis() { return Point3Temp.make(this.defaultOrientationAxis); } public float getDefaultOrientation() { return this.defaultOrientation; } @Override protected final void noteAddingTo(SuperRoot s) { World w = (World)s; } public RoomEnvironment getEnvironment() { return this.environment; } public Vector getOutgoingPortals() { return this.outgoingPortals; } public void addOutgoingPortal(Portal p) { if (!this.outgoingPortals.contains(p)) { this.outgoingPortals.addElement(p); for (int index = 0; index < this.aFilters.size(); index++) { this.aFilters.elementAt(index).moveEmitter(); } } } public void removeOutgoingPortal(Portal p) { this.outgoingPortals.removeElement(p); Vector v = this.getAFilters(); for (int v_index = 0; v_index < v.size(); v_index++) { v.elementAt(v_index).moveEmitter(); } } public Vector getAFilters() { return this.aFilters; } public void addAFilter(AudibilityFilter p) { this.aFilters.addElement(p); } public void removeAFilter(AudibilityFilter a) { this.aFilters.removeElement(a); } public void move(Room from, Room to) { Vector v = from.getAFilters(); for (int index = 0; index < v.size(); index++) { v.elementAt(index).updateListenerState(); } v = to.getAFilters(); for (int index = 0; index < v.size(); index++) { v.elementAt(index).updateListenerState(); } } static native int addLight(int var0, float var1, float var2, float var3, float var4, float var5, float var6); static native void setLightPosition(int var0, float var1, float var2, float var3); static native void setLightColor(int var0, float var1, float var2, float var3); private void setLightPosition(float x, float y, float z) { if (this.lightid != 0) { setLightPosition(this.lightid, x, y, z); } if (this.lightid2 != 0) { setLightPosition(this.lightid2, -x, -y, -z); } } private void setLightColor(float red, float green, float blue) { if (this.lightid != 0) { setLightColor(this.lightid, red, green, blue); } if (this.lightid2 != 0) { setLightColor(this.lightid2, red / 2.0F, green / 2.0F, blue / 2.0F); } } public void setLightPosition(Point3Temp p) { this.lightPosition.set(p.x, p.y, p.z); this.setLightPosition(p.x, p.y, p.z); RoomEnvironment re = this.getEnvironment(); if (re != null) { re.setLightPosition(p.x, p.y, p.z); } re = this.getInfiniteBackground(); if (re != null) { re.setLightPosition(p.x, p.y, p.z); } } public void setLightColor(Color c) { this.lightColor = new Color(c.getRGB()); float r = c.getRed() / 256.0F; float g = c.getGreen() / 256.0F; float b = c.getBlue() / 256.0F; this.setLightColor(r, g, b); RoomEnvironment re = this.getEnvironment(); if (re != null) { re.setLightColor(r, g, b); } re = this.getInfiniteBackground(); if (re != null) { re.setLightColor(r, g, b); } } public Point3Temp getLightPosition() { return Point3Temp.make(this.lightPosition); } public Color getLightColor() { return new Color(this.lightColor.getRGB()); } public void noteRef() { this.renderStamp = Std.getRealTime(); World w = this.getWorld(); if (w != null) { w.incRef(); } } public void discardIfOld() { if (this.hasClump()) { int age = Std.getFastTime() - this.renderStamp; if (age > timeoutAge) { this.markVoid(); } } } public void generateFrameEvents(FrameEvent f) { this.noteRef(); if (Main.profile != 0) { for (int i = 0; i < this.frameHandlers.size(); i++) { FrameHandler fh = this.frameHandlers.elementAt(i); WObject fhw = this.frameHandlerWObjects.elementAt(i); int start = Std.getRealTime(); long startBytes = Runtime.getRuntime().freeMemory(); f.retargetAndDeliver(fh, fhw); int dur = Std.getRealTime() - start; long used = startBytes - Runtime.getRuntime().freeMemory(); if (dur > Main.profile) { if (fh instanceof SuperRoot) { System.out.println("Took " + dur + "ms and " + used + " bytes to call frameHandler " + ((SuperRoot)fh).getName() + " of " + fhw.getName()); } else { System.out.println("Took " + dur + "ms and " + used + " bytes to call frameHandler " + fh + " of " + fhw.getName()); } } } } else { for (int ix = 0; ix < this.frameHandlers.size(); ix++) { f.retargetAndDeliver(this.frameHandlers.elementAt(ix), this.frameHandlerWObjects.elementAt(ix)); } } if (this.teleportChain != null && this.teleportInterval != -1 && !this.teleported && Std.getSynchronizedTime() % this.teleportInterval == 0) { this.teleported = true; TeleportAction.teleport(this.teleportChain, this); } } @Override public void teleportStatus(String err, String dest) { if (err != null) { this.teleported = false; } } private int findFrameHandler(FrameHandler h, WObject o) { int next = 0; while (true) { next = this.frameHandlers.indexOf(h, next); if (next == -1) { return -1; } if (this.frameHandlerWObjects.elementAt(next) == o) { return next; } next++; } } void addFrameHandler(FrameHandler h, WObject o) { int i = this.findFrameHandler(h, o); if (i < 0) { this.frameHandlers.addElement(h); this.frameHandlerWObjects.addElement(o); } } void removeFrameHandler(FrameHandler h, WObject o) { int i = this.findFrameHandler(h, o); if (i >= 0) { this.frameHandlers.removeElementAt(i); this.frameHandlerWObjects.removeElementAt(i); } } native void createScene(); native void destroyScene(); @Override protected void markVoid() { this.infiniteBackground.markVoid(); this.environment.markVoid(); super.markVoid(); this.destroyScene(); this.lightid = 0; this.lightid2 = 0; } @Override protected void noteTransformChange() { super.noteTransformChange(); this.getInfiniteBackground().setTransform(this); this.getEnvironment().setTransform(this); int i = this.outgoingPortals.size(); while (--i >= 0) { this.outgoingPortals.elementAt(i).discardTransform(); } } public void setSkyColor(Color c) { this.skyColor = c; } public Color getSkyColor() { return this.skyColor; } public void setGroundColor(Color c) { this.groundColor = c; } public Color getGroundColor() { return this.groundColor; } public boolean getVIPOnly() { return (this.flags & isVIPOnly) != 0; } public void prerender(Camera cam, float x, float y, float z, float d) { this.flags = this.flags | isRendering; this.infiniteBackground.prerender(); this.environment.prerender(); Pilot.addVisibleRoom(this, x, y, z, d); int end = this.prerenderHandlers.size(); for (int i = 0; i < end; i++) { Prerenderable p = this.prerenderHandlers.elementAt(i); p.prerender(cam); } } public void addPrerenderHandler(Prerenderable o) { assert (this.flags & isRendering) == 0; if (!this.prerenderHandlers.contains(o)) { this.prerenderHandlers.addElement(o); } } public void prependPrerenderHandler(Prerenderable o) { assert (this.flags & isRendering) == 0; if (!this.prerenderHandlers.contains(o)) { this.prerenderHandlers.insertElementAt(o, 0); } } public void removePrerenderHandler(Prerenderable o) { assert (this.flags & isRendering) == 0; this.prerenderHandlers.removeElement(o); } public void postrender(Camera cam, float x, float y, float z, float d) { Pilot.addVisibleRoom(this, x, y, z, d); int i = this.postrenderHandlers.size(); while (--i >= 0) { Postrenderable p = this.postrenderHandlers.elementAt(i); p.postrender(cam); } this.flags = this.flags & ~isRendering; } public void addPostrenderHandler(Postrenderable o) { assert (this.flags & isRendering) == 0; if (!this.postrenderHandlers.contains(o)) { this.postrenderHandlers.addElement(o); } } public void prependPostrenderHandler(Postrenderable o) { assert (this.flags & isRendering) == 0; if (!this.postrenderHandlers.contains(o)) { this.postrenderHandlers.insertElementAt(o, 0); } } public void removePostrenderHandler(Postrenderable o) { assert (this.flags & isRendering) == 0; this.postrenderHandlers.removeElement(o); } @Override public void detectBump(BumpEventTemp b) { if (this.environment.getBumpable()) { this.environment.detectBump(b); } super.detectBump(b); } @Override public BoundBoxTemp getBoundBox() { return BoundBoxTemp.make(Point3Temp.make(), Point3Temp.make()); } @Override public World getWorld() { return (World)this.getOwner(); } @Override public Room getRoom() { return this; } @Override public Room getRoomFromClump() { return this; } @Override public Room getRoomNotFromClump() { return this; } public Galaxy getGalaxy() { World w = this.getWorld(); return w == null ? null : w.getConsole().getGalaxy(); } public String getURL() { return this.getWorld().getSourceURL().getAbsolute() + "#" + this.getName(); } public boolean registerShare(WObject w) { w.notifyRegister(1); return true; } private void processPendingRegistrations() { this.notifyRegister(1); } public NetworkRoom getNetworkRoom() { return this._netRoom; } void subscribe(RoomSubscribeInfo info) { assert this._netRoom != null; this._netRoom.subscribe(info); } void subscribeDist(RoomSubscribeInfo info) { assert this._netRoom != null; this._netRoom.subscribeDist(info); } void unsubscribe() { if (this._netRoom != null) { this._netRoom.unsubscribe(); } else { System.out.println(this + ": unsubscribing from bad room?"); new Exception().printStackTrace(System.out); } } public boolean getAllowTeleport() { return this.allowTeleport; } @Override public Object properties(int index, int offset, int mode, Object value) throws NoSuchPropertyException { Object ret = null; switch (index - offset) { case 0: if (mode == 0) { ret = new Property(this, index, "Environment"); } else if (mode == 1) { ret = this.environment; } break; case 1: if (mode == 0) { ret = new Property(this, index, "Infinite Background"); } else if (mode == 1) { ret = this.infiniteBackground; } break; case 2: if (mode == 0) { ret = ColorPropertyEditor.make(new Property(this, index, "Sky Color").allowSetNull()); if (this.getSkyColor() == null) { ret = MaybeNullPropertyEditor.make((Property)ret, Color.blue); } } else if (mode == 1) { ret = this.getSkyColor(); } else if (mode == 2) { this.setSkyColor((Color)value); } break; case 3: if (mode == 0) { ret = ColorPropertyEditor.make(new Property(this, index, "Ground Color").allowSetNull()); if (this.getGroundColor() == null) { ret = MaybeNullPropertyEditor.make((Property)ret, Color.green); } } else if (mode == 1) { ret = this.getGroundColor(); } else if (mode == 2) { this.setGroundColor((Color)value); } break; case 4: if (mode == 0) { ret = Point3PropertyEditor.make(new Property(this, index, "Default Position")); } else if (mode == 1) { ret = new Point3(this.getDefaultPosition()); } else if (mode == 2) { this.defaultPosition = (Point3)value; } break; case 5: if (mode == 0) { ret = Point3PropertyEditor.make(new Property(this, index, "Default Orientation Axis")); } else if (mode == 1) { ret = new Point3(this.getDefaultOrientationAxis()); } else if (mode == 2) { this.defaultOrientationAxis = (Point3)value; } break; case 6: if (mode == 0) { ret = FloatPropertyEditor.make(new Property(this, index, "Default Orientation Angle")); } else if (mode == 1) { ret = new Float(this.getDefaultOrientation()); } else if (mode == 2) { this.defaultOrientation = (Float)value; } break; case 7: if (mode == 0) { ret = BooleanPropertyEditor.make(new Property(this, index, "VIP only"), "No", "Yes"); } else if (mode == 1) { ret = new Boolean((this.flags & isVIPOnly) != 0); } else if (mode == 2) { if ((Boolean)value) { this.flags = this.flags | isVIPOnly; } else { this.flags = this.flags & ~isVIPOnly; } } break; case 8: if (mode == 0) { ret = BooleanPropertyEditor.make(new Property(this, index, "Visible to VIP only"), "No", "Yes"); } else if (mode == 1) { ret = new Boolean((this.flags & isVIPViewOnly) != 0); } else if (mode == 2) { if ((Boolean)value) { this.flags = this.flags | isVIPViewOnly; } else { this.flags = this.flags & ~isVIPViewOnly; } } break; case 9: if (mode == 0) { ret = Point3PropertyEditor.make(new Property(this, index, "Light Source Direction")); } else if (mode == 1) { ret = this.lightPosition; } else if (mode == 2) { this.setLightPosition((Point3)value); } break; case 10: if (mode == 0) { ret = ColorPropertyEditor.make(new Property(this, index, "Light Color")); } else if (mode == 1) { ret = this.lightColor; } else if (mode == 2) { this.setLightColor((Color)value); } break; case 11: if (mode == 0) { ret = IntegerPropertyEditor.make(new Property(this, index, "Time To Auto-Teleport (seconds, -1 = never)")); } else if (mode == 1) { ret = new Integer(this.teleportInterval); } else if (mode == 2) { this.teleportInterval = (Integer)value; } break; case 12: if (mode == 0) { ret = StringPropertyEditor.make(new Property(this, index, "Auto-Teleport destination")); } else if (mode == 1) { ret = this.teleportChain; } else if (mode == 2) { this.teleportChain = new String((String)value); } break; case 13: if (mode == 0) { ret = BooleanPropertyEditor.make(new Property(this, index, "Allow Teleporting"), "No", "Yes"); } else if (mode == 1) { ret = new Boolean(this.allowTeleport); } else if (mode == 2) { this.allowTeleport = (Boolean)value; } break; default: ret = super.properties(index, offset + 14, mode, value); } return ret; } @Override public void saveState(Saver s) throws IOException { s.saveVersion(7, classCookie); super.saveState(s); if (this.skyColor != null) { s.saveBoolean(true); s.saveInt(this.skyColor.getRGB()); } else { s.saveBoolean(false); } if (this.groundColor != null) { s.saveBoolean(true); s.saveInt(this.groundColor.getRGB()); } else { s.saveBoolean(false); } s.save(this.defaultPosition); s.save(this.defaultOrientationAxis); s.saveFloat(this.defaultOrientation); s.save(this.lightPosition); s.saveInt(this.lightColor.getRGB()); s.save(this.environment); s.save(this.infiniteBackground); s.saveString(this.teleportChain); s.saveInt(this.teleportInterval); s.saveBoolean(this.allowTeleport); } @Override public void restoreState(Restorer r) throws IOException, TooNewException { switch (r.restoreVersion(classCookie)) { case 0: r.setOldFlag(); super.restoreState(r); this.setName(r.restoreString()); r.restore(); if (r.restoreBoolean()) { this.skyColor = new Color(r.restoreInt()); } if (r.restoreBoolean()) { this.groundColor = new Color(r.restoreInt()); } r.restoreMaybeNull(); this.defaultPosition = (Point3)r.restore(); this.defaultOrientationAxis = (Point3)r.restore(); this.defaultOrientation = r.restoreFloat(); r.restoreVector(); this.environment = (RoomEnvironment)r.restore(); this.infiniteBackground = new RoomEnvironment(); break; case 1: r.setOldFlag(); super.restoreState(r); r.restore(); if (r.restoreBoolean()) { this.skyColor = new Color(r.restoreInt()); } if (r.restoreBoolean()) { this.groundColor = new Color(r.restoreInt()); } r.restoreMaybeNull(); this.defaultPosition = (Point3)r.restore(); this.defaultOrientationAxis = (Point3)r.restore(); this.defaultOrientation = r.restoreFloat(); this.environment = (RoomEnvironment)r.restore(); this.infiniteBackground = new RoomEnvironment(); break; case 2: r.setOldFlag(); super.restoreState(r); if (r.restoreBoolean()) { this.skyColor = new Color(r.restoreInt()); } if (r.restoreBoolean()) { this.groundColor = new Color(r.restoreInt()); } r.restoreMaybeNull(); this.defaultPosition = (Point3)r.restore(); this.defaultOrientationAxis = (Point3)r.restore(); this.defaultOrientation = r.restoreFloat(); this.environment = (RoomEnvironment)r.restore(); this.infiniteBackground = new RoomEnvironment(); break; case 3: r.setOldFlag(); super.restoreState(r); if (r.restoreBoolean()) { this.skyColor = new Color(r.restoreInt()); } if (r.restoreBoolean()) { this.groundColor = new Color(r.restoreInt()); } r.restoreMaybeNull(); this.defaultPosition = (Point3)r.restore(); this.defaultOrientationAxis = (Point3)r.restore(); this.defaultOrientation = r.restoreFloat(); this.environment = (RoomEnvironment)r.restore(); this.infiniteBackground = (RoomEnvironment)r.restore(); break; case 4: r.setOldFlag(); super.restoreState(r); if (r.restoreBoolean()) { this.skyColor = new Color(r.restoreInt()); } if (r.restoreBoolean()) { this.groundColor = new Color(r.restoreInt()); } this.defaultPosition = (Point3)r.restore(); this.defaultOrientationAxis = (Point3)r.restore(); this.defaultOrientation = r.restoreFloat(); this.environment = (RoomEnvironment)r.restore(); this.infiniteBackground = (RoomEnvironment)r.restore(); break; case 5: super.restoreState(r); if (r.restoreBoolean()) { this.skyColor = new Color(r.restoreInt()); } if (r.restoreBoolean()) { this.groundColor = new Color(r.restoreInt()); } this.defaultPosition = (Point3)r.restore(); this.defaultOrientationAxis = (Point3)r.restore(); this.defaultOrientation = r.restoreFloat(); this.setLightPosition((Point3)r.restore()); this.setLightColor(new Color(r.restoreInt())); this.environment = (RoomEnvironment)r.restore(); this.infiniteBackground = (RoomEnvironment)r.restore(); break; case 6: super.restoreState(r); if (r.restoreBoolean()) { this.skyColor = new Color(r.restoreInt()); } if (r.restoreBoolean()) { this.groundColor = new Color(r.restoreInt()); } this.defaultPosition = (Point3)r.restore(); this.defaultOrientationAxis = (Point3)r.restore(); this.defaultOrientation = r.restoreFloat(); this.setLightPosition((Point3)r.restore()); this.setLightColor(new Color(r.restoreInt())); this.environment = (RoomEnvironment)r.restore(); this.infiniteBackground = (RoomEnvironment)r.restore(); this.teleportChain = r.restoreString(); this.teleportInterval = r.restoreInt(); this.roomLoadTime = Std.getFastTime() / 1000; break; case 7: super.restoreState(r); if (r.restoreBoolean()) { this.skyColor = new Color(r.restoreInt()); } if (r.restoreBoolean()) { this.groundColor = new Color(r.restoreInt()); } this.defaultPosition = (Point3)r.restore(); this.defaultOrientationAxis = (Point3)r.restore(); this.defaultOrientation = r.restoreFloat(); this.setLightPosition((Point3)r.restore()); this.setLightColor(new Color(r.restoreInt())); this.environment = (RoomEnvironment)r.restore(); this.infiniteBackground = (RoomEnvironment)r.restore(); this.teleportChain = r.restoreString(); this.teleportInterval = r.restoreInt(); this.roomLoadTime = Std.getFastTime() / 1000; this.allowTeleport = r.restoreBoolean(); break; default: throw new TooNewException(); } super.add(this.environment); super.add(this.infiniteBackground); } public void aboutToDraw() { if (!this.hasClump() && this.getOwner() instanceof World) { this.recursiveAddRwChildren(null); } } @Override protected void addRwChildren(WObject parent) { assert parent == null; this.createClump(); this.createScene(); this.newRwChildHelper(); float red = this.lightColor.getRed() / 256.0F; float green = this.lightColor.getGreen() / 256.0F; float blue = this.lightColor.getBlue() / 256.0F; this.lightid = addLight(this.sceneID, this.lightPosition.x, this.lightPosition.y, this.lightPosition.z, red, green, blue); this.lightid2 = addLight(this.sceneID, -this.lightPosition.x, -this.lightPosition.y, -this.lightPosition.z, red * 0.5F, green * 0.5F, blue * 0.5F); } @Override public void recursiveAddRwChildren(WObject parent) { super.recursiveAddRwChildren(parent); this.environment.recursiveAddRwChildren(this); this.infiniteBackground.recursiveAddRwChildren(this); } public final float floorHeight(float x, float y) { return this.floorHeight(x, y, 120.0F); } public float floorHeight(float x, float y, float z) { Transform inverse = this.getObjectToWorldMatrix().invert(); Point3Temp p = Point3Temp.make(x, y, z).times(inverse); inverse.recycle(); x = p.x; y = p.y; z = p.z; float height = 0.0F; boolean patchFound = false; Enumeration e = (Enumeration)this.getContents(); while (e.hasMoreElements()) { Object o = e.nextElement(); if (o instanceof FloorPatch && ((FloorPatch)o).inPatch(x, y)) { float patchHeight = ((FloorPatch)o).floorHeight(x, y); if (patchHeight <= z && (patchHeight > height || !patchFound)) { patchFound = true; height = ((FloorPatch)o).floorHeight(x, y); } } } return height; } public Point3 surfaceNormal(float x, float y, float z) { Point3 normal = new Point3(0.0F, 0.0F, 1.0F); FloorPatch patch = null; float height = 0.0F; boolean patchFound = false; Enumeration e = (Enumeration)this.getContents(); while (e.hasMoreElements()) { Object o = e.nextElement(); if (o instanceof FloorPatch && ((FloorPatch)o).inPatch(x, y)) { float patchHeight = ((FloorPatch)o).floorHeight(x, y); if (patchHeight <= z && (patchHeight > height || !patchFound)) { patchFound = true; height = ((FloorPatch)o).floorHeight(x, y); patch = (FloorPatch)o; } } } if (patch != null) { normal = patch.surfaceNormal(x, y); } normal.vectorTimes(this.getObjectToWorldMatrix()); return normal; } public RoomEnvironment getInfiniteBackground() { return this.infiniteBackground; } @Override public String toString() { return this.getWorld() + "#" + this.getName(); } }