diff options
| author | Fuwn <[email protected]> | 2026-02-12 22:33:32 -0800 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2026-02-12 22:33:32 -0800 |
| commit | c7a9d4a6bd53ed7d61731770f2f10e8b9fd435f9 (patch) | |
| tree | df9f48bf128a6c0186a8e91857d6ff30fe0e9f18 /NET/worlds/scape/Shape.java | |
| download | worldsplayer-c7a9d4a6bd53ed7d61731770f2f10e8b9fd435f9.tar.xz worldsplayer-c7a9d4a6bd53ed7d61731770f2f10e8b9fd435f9.zip | |
Initial commit
Diffstat (limited to 'NET/worlds/scape/Shape.java')
| -rw-r--r-- | NET/worlds/scape/Shape.java | 557 |
1 files changed, 557 insertions, 0 deletions
diff --git a/NET/worlds/scape/Shape.java b/NET/worlds/scape/Shape.java new file mode 100644 index 0000000..597b404 --- /dev/null +++ b/NET/worlds/scape/Shape.java @@ -0,0 +1,557 @@ +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<ShapeLoaderListener> 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<Texture> 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<ShapeLoaderListener>(); + } + + if (!this.listeners.contains(l)) { + this.listeners.addElement(l); + } + } + + private void notifyLoadListeners() { + if (this.listeners != null) { + Enumeration<ShapeLoaderListener> 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<Texture> 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<WObject> detachList = new Vector<WObject>(); + Enumeration<Object> e = (Enumeration<Object>)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 = 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<Texture> 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 + "]"; + } +} |