package NET.worlds.scape; import NET.worlds.console.Console; import NET.worlds.console.DefaultConsole; import NET.worlds.console.Main; import NET.worlds.console.MainCallback; import NET.worlds.console.StatMemNode; import NET.worlds.core.Archive; import NET.worlds.core.IniFile; import NET.worlds.network.URL; import java.awt.PopupMenu; import java.io.IOException; import java.net.MalformedURLException; import java.util.Enumeration; import java.util.StringTokenizer; import java.util.Vector; public class Shape extends WObject implements MainCallback, Animatable, MouseDownHandler { static int disableLOD = IniFile.gamma().getIniInt("DisableLOD", 1); static int forceLODLevel; Vector listeners = null; URL url; boolean mustReload; boolean recomputeLODs; int numDetailLevels; int currentLOD; int lastLOD; URL[] lodURLs; float[] lodDistanceTriggers; float[] lodAreas; protected static int NORMAL; protected static int ERROR; protected static int LOADING; private int pendingShape; boolean isDefault; static URL xShape; URL realFile; private boolean prepareRoom; private Material animatableMaterial; protected int animatableClumpID; Vector textures; private static Object classCookie; static { if (ProgressiveAdder.get().enabled()) { disableLOD = 1; } if (disableLOD == 1) { System.out.println("Avatar dynamic LOD disabled."); } else { System.out.println("Using avatar dynamic LOD when available."); } StatMemNode smn = StatMemNode.getNode(); forceLODLevel = IniFile.gamma().getIniInt("LowResAvs", -1); if (forceLODLevel != -1) { System.out.println("Avatar LOD's forced to level " + forceLODLevel); disableLOD = 0; } int lowResThreshold = IniFile.gamma().getIniInt("ForceLowResRAMLimit", 33554432); smn.updateMemoryStatus(); if (smn._totPhysMem <= lowResThreshold && smn._totPhysMem > 0) { System.out.println("Low memory detected, using LOD 2 for all avatars."); forceLODLevel = 2; disableLOD = 0; } NORMAL = 0; ERROR = -1; LOADING = -2; xShape = URL.make("home:avatar.rwg"); nativeInit(); classCookie = new Object(); } public Shape() { this.pendingShape = NORMAL; this.isDefault = false; this.numDetailLevels = 0; this.recomputeLODs = true; this.currentLOD = this.lastLOD = 0; } void addLoadListener(ShapeLoaderListener l) { if (this.listeners == null) { this.listeners = new Vector(); } if (!this.listeners.contains(l)) { this.listeners.addElement(l); } } private void notifyLoadListeners() { if (this.listeners != null) { Enumeration e = this.listeners.elements(); if (e != null) { while (e.hasMoreElements()) { ShapeLoaderListener l = e.nextElement(); l.notifyShapeLoaded(this); } } this.listeners.removeAllElements(); } } void removeLoadListener(ShapeLoaderListener l) { if (this.listeners != null) { this.listeners.removeElement(l); } } public void setBaseLODURL(URL baseURL) { if (disableLOD == 0 && baseURL != null && baseURL.getAbsolute().startsWith("avatar:")) { int colon = baseURL.toString().indexOf(58); int dot = baseURL.toString().indexOf(46); String avName = baseURL.toString().substring(colon + 1, dot); String avURLName = "avatar:lod/" + avName + ".lod"; URL lodURL; try { lodURL = new URL(avURLName); } catch (MalformedURLException var16) { return; } byte[] lodFile = Archive.readTextFile(lodURL.unalias()); if (lodFile != null) { String lodText = new String(lodFile); StringTokenizer tk = new StringTokenizer(lodText); this.numDetailLevels = Integer.parseInt(tk.nextToken()); this.lodURLs = new URL[this.numDetailLevels]; this.lodAreas = new float[this.numDetailLevels]; this.lodDistanceTriggers = new float[this.numDetailLevels]; int x = 0; this.lodURLs[x] = baseURL; for (this.lodAreas[x++] = 0.0F; tk.hasMoreTokens(); x++) { String level = tk.nextToken(); String value = tk.nextToken(); Float f = new Float(value); this.lodAreas[x] = f; try { this.lodURLs[x] = new URL("avatar:lod/" + avName + level + baseURL.toString().substring(dot)); } catch (MalformedURLException var15) { System.out.println("Error creating lod URL!\n"); this.numDetailLevels = 0; return; } } } } this.recomputeLODs = true; } public native float calcLODDistance(float var1); public synchronized boolean setLOD(float dist) { boolean switched = false; if (this.numDetailLevels > 0) { if (forceLODLevel != -1) { int realLOD = forceLODLevel > this.numDetailLevels - 1 ? this.numDetailLevels - 1 : forceLODLevel; if (realLOD != this.lastLOD) { this.setURL(this.lodURLs[realLOD]); this.currentLOD = this.lastLOD = realLOD; return true; } return false; } if (this.recomputeLODs) { for (int x = 0; x < this.numDetailLevels; x++) { this.lodDistanceTriggers[x] = this.calcLODDistance(this.lodAreas[x]); } this.recomputeLODs = false; } for (int lod = 0; lod < this.numDetailLevels; lod++) { if (this.lodDistanceTriggers[lod] > dist) { if (lod != this.lastLOD) { this.currentLOD = lod; if (this instanceof PosableShape) { ((PosableShape)this).removeSubparts(); } if (this.isLoaded()) { this.releasePendingShape(); } this.setURL(this.lodURLs[lod]); } break; } } } if (this.lastLOD != this.currentLOD) { switched = true; } this.lastLOD = this.currentLOD; return switched; } public URL getURL() { return this.url; } synchronized boolean isLoaded() { return this.pendingShape < -2 || this.pendingShape > 0; } public boolean isFullyLoaded() { return this.pendingShape == NORMAL && this.hasClump(); } @Override public boolean handle(MouseDownEvent e) { SuperRoot ultimateOwner = this; while (ultimateOwner.getOwner() != null) { ultimateOwner = ultimateOwner.getOwner(); if (ultimateOwner instanceof PosableShape && ultimateOwner.getOwner() instanceof PosableDrone) { return false; } } PopupMenu m = new PopupMenu(); if (AnimatedActionManager.get().buildActionMenu(m, this)) { Console c = Console.getActive(); if (c instanceof DefaultConsole) { DefaultConsole dc = (DefaultConsole)c; if (dc.getRender() != null) { dc.getRender().add(m); m.addActionListener(AnimatedActionManager.get()); m.show(dc.getRender(), e.x, e.y); return true; } } } m = WorldScriptManager.getInstance().shapeClicked(this); if (m != null) { Console c = Console.getActive(); if (c instanceof DefaultConsole) { DefaultConsole dc = (DefaultConsole)c; if (dc.getRender() != null) { dc.getRender().add(m); m.addActionListener(WorldScriptManager.getInstance()); m.show(dc.getRender(), e.x, e.y); return true; } } } return false; } void setState(int mode, Vector texs) { if (this.isLoaded()) { this.releasePendingShape(); } this.releaseTextures(); this.pendingShape = mode; if (this.isLoaded()) { this.shapeRedraw(); } this.textures = texs; } public synchronized void setURL(URL newName) { if (newName != this.url && (newName == null || !newName.equals(this.url))) { this.url = newName; this.realFile = null; boolean mrWas = this.mustReload; this.mustReload = true; this.setState(NORMAL, null); this.shapeRedraw(); this.mustReload = mrWas; } } void shapeRedraw() { if (this.hasClump()) { this.reclump(); } this.notifyLoadListeners(); } boolean isAv() { return this.url != null && this.url.getAbsolute().startsWith("avatar:"); } static String getBodBase(URL u) { String s = u.getBase(); int len = s.length(); return s.endsWith(".bod") && len >= 6 ? s.substring(0, len - 6) : null; } int getBodPartNum() { String s = this.url.getInternal(); int len = s.length(); return s.endsWith(".bod") && len >= 6 ? (s.charAt(len - 6) - 48) * 10 + (s.charAt(len - 5) - 48) : 0; } @Override protected synchronized void addRwChildren(WObject container) { assert !this.hasClump(); if (this.pendingShape == NORMAL && this.url != null) { String urlStr; if ((urlStr = this.url.getAbsolute()).startsWith("system:subclump")) { SuperRoot o = this.getOwner(); if (o instanceof Shape) { int num = 0; try { num = new Integer(urlStr.substring(15)); } catch (NumberFormatException var6) { } this.animatableClumpID = ((Shape)o).extractSubclump(num); this.clumpID = addEmptyParentClump(this.animatableClumpID); this.isDefault = false; this.newRwClumpChildHelper(container); return; } this.pendingShape = ERROR; } else { this.setState(-2, null); this.isDefault = false; this.realFile = this.isAv() ? xShape : this.url; if (this.url.endsWith(".bod")) { String s = getBodBase(this.url); this.realFile = URL.make(this.url, s + ".bod"); } BackgroundLoader.get(new ShapeLoader(this), this.realFile); } } if (!this.isLoaded()) { this.clumpID = makeDefaultShape(); this.isDefault = true; } else { this.clumpID = this.pendingShape; this.pendingShape = NORMAL; this.isDefault = false; } this.newRwClumpChildHelper(container); } @Override public void recursiveAddRwChildren(WObject parent) { if (this.animatableMaterial != null && this.animatableClumpID == 0) { this.animatableMaterial.addRwChildren(); } super.recursiveAddRwChildren(parent); if (this.animatableMaterial != null) { this.nativeSetMaterial(this.animatableMaterial); } } public Material getMaterial() { return this.animatableMaterial; } @Override protected void voidClump() { if (!this.mustReload && !this.isDefault && this.pendingShape == NORMAL) { this.pendingShape = this.extractClump(); } else { if (this.isLoaded()) { this.releasePendingShape(); } this.releaseTextures(); super.voidClump(); this.animatableClumpID = 0; if (this.animatableMaterial != null) { this.animatableMaterial.markVoid(); } } } @Override public void discard() { if (this.isLoaded()) { this.releasePendingShape(); } this.releaseTextures(); super.discard(); } public void makeSpecials() { if (!this.prepareRoom) { Vector detachList = new Vector(); Enumeration e = (Enumeration)this.getRoom().getContents(); while (e.hasMoreElements()) { Object p = e.nextElement(); if (p instanceof WObject) { WObject w = (WObject)p; if (w.getAutobuilt()) { detachList.addElement(w); } } } e = (Enumeration)detachList.elements(); while (e.hasMoreElements()) { ((WObject)e.nextElement()).detach(); } Main.register(this); this.prepareRoom = true; } } @Override public void mainCallback() { if (this.hasClump()) { convertSpecial(this.clumpID); this.prepareRoom = false; Main.unregister(this); } } @Override public void setMaterial(Material m) { if (this.animatableMaterial != null) { this.animatableMaterial.detach(); } this.add(m); this.animatableMaterial = m; this.nativeSetMaterial(m); } public native void nativeSetMaterial(Material var1); protected native int extractSubclump(int var1); protected static native int addEmptyParentClump(int var0); private static native void convertSpecial(int var0); public static native void nativeInit(); private static native int makeDefaultShape(); private native void releasePendingShape(); private void releaseTextures() { if (this.textures != null) { Enumeration en = this.textures.elements(); while (en.hasMoreElements()) { en.nextElement().decRef(); } this.textures = null; } } @Override protected void finalize() { if (this.isLoaded()) { this.releasePendingShape(); } this.releaseTextures(); super.finalize(); } public void setReload(boolean mustReload) { this.mustReload = mustReload; } @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 = URLPropertyEditor.make(new Property(this, index, "File"), "rwx;rwg;bod"); } else if (mode == 1) { ret = this.url; } else if (mode == 2) { URL newName = (URL)value; if (newName != null && newName.equals(this.url)) { this.setURL(null); } this.setURL(newName); } break; case 1: if (mode == 0) { ret = BooleanPropertyEditor.make(new Property(this, index, "PrepareRoom"), "No", "Yes"); } else if (mode == 1) { ret = new Boolean(this.prepareRoom); } else if (mode == 2 && (Boolean)value) { this.makeSpecials(); } break; default: ret = super.properties(index, offset + 2, mode, value); } return ret; } @Override public void saveState(Saver s) throws IOException { s.saveVersion(1, classCookie); super.saveState(s); URL.save(s, this.url); } @Override public void restoreState(Restorer r) throws IOException, TooNewException { switch (r.restoreVersion(classCookie)) { case 0: case 1: super.restoreState(r); URL url = URL.restore(r); if (url != null) { this.setURL(url); } return; default: throw new TooNewException(); } } @Override public String toString() { return super.toString() + "[" + this.url + "]"; } }