package com.volmit.iris.gui; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.Point; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionListener; import java.awt.event.MouseWheelEvent; import java.awt.event.MouseWheelListener; import java.awt.image.BufferedImage; import java.awt.image.ImageObserver; import java.io.File; import java.io.IOException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import javax.imageio.ImageIO; import javax.swing.JFrame; import javax.swing.JPanel; import com.volmit.iris.Iris; import com.volmit.iris.gen.IrisTerrainProvider; import com.volmit.iris.util.BlockPosition; import com.volmit.iris.util.J; import com.volmit.iris.util.KMap; import com.volmit.iris.util.KSet; import com.volmit.iris.util.O; import com.volmit.iris.util.PrecisionStopwatch; import com.volmit.iris.util.RollingSequence; public class IrisVision extends JPanel implements MouseWheelListener { private static final long serialVersionUID = 2094606939770332040L; private IrisRenderer renderer; private int posX = 0; private int posZ = 0; private double scale = 128; private double mscale = 1D; private int w = 0; private int h = 0; private double lx = Double.MAX_VALUE; private double lz = Double.MAX_VALUE; private double ox = 0; private double oz = 0; private double oxp = 0; private double ozp = 0; private short lid = -1; double tfps = 240D; private RollingSequence rs = new RollingSequence(512); private O m = new O<>(); private int tid = 0; private KMap positions = new KMap<>(); private KMap fastpositions = new KMap<>(); private KSet working = new KSet<>(); private KSet workingfast = new KSet<>(); private final ExecutorService e = Executors.newFixedThreadPool(8, new ThreadFactory() { @Override public Thread newThread(Runnable r) { tid++; Thread t = new Thread(r); t.setName("Iris HD Renderer " + tid); t.setPriority(Thread.MIN_PRIORITY); t.setUncaughtExceptionHandler((et, e) -> { Iris.info("Exception encountered in " + et.getName()); e.printStackTrace(); }); return t; } }); private final ExecutorService eh = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), new ThreadFactory() { @Override public Thread newThread(Runnable r) { tid++; Thread t = new Thread(r); t.setName("Iris Renderer " + tid); t.setPriority(Thread.NORM_PRIORITY); t.setUncaughtExceptionHandler((et, e) -> { Iris.info("Exception encountered in " + et.getName()); e.printStackTrace(); }); return t; } }); public IrisVision() { m.set(8); renderer = new IrisRenderer(null); rs.put(1); addMouseWheelListener((MouseWheelListener) this); addMouseMotionListener(new MouseMotionListener() { @Override public void mouseMoved(MouseEvent e) { Point cp = e.getPoint(); lx = (cp.getX()); lz = (cp.getY()); } @Override public void mouseDragged(MouseEvent e) { Point cp = e.getPoint(); ox += (lx - cp.getX()) * scale; oz += (lz - cp.getY()) * scale; lx = cp.getX(); lz = cp.getY(); } }); } public BufferedImage getTile(KSet fg, int div, int x, int z, O m) { BlockPosition key = new BlockPosition((int) mscale, Math.floorDiv(x, div), Math.floorDiv(z, div)); fg.add(key); if(positions.containsKey(key)) { return positions.get(key); } if(fastpositions.containsKey(key)) { if(!working.contains(key) && working.size() < 9) { m.set(m.get() - 1); if(m.get() >= 0) { working.add(key); double mk = mscale; double mkd = scale; short l = lid; e.submit(() -> { PrecisionStopwatch ps = PrecisionStopwatch.start(); BufferedImage b = renderer.render(x * mscale, z * mscale, div * mscale, div); rs.put(ps.getMilliseconds()); working.remove(key); if(mk == mscale && mkd == scale && lid == l) { positions.put(key, b); } }); } } return fastpositions.get(key); } if(workingfast.contains(key)) { return null; } m.set(m.get() - 1); if(m.get() >= 0) { workingfast.add(key); double mk = mscale; double mkd = scale; short l = lid; eh.submit(() -> { PrecisionStopwatch ps = PrecisionStopwatch.start(); BufferedImage b = renderer.render(x * mscale, z * mscale, div * mscale, div / 12); rs.put(ps.getMilliseconds()); workingfast.remove(key); if(mk == mscale && mkd == scale && lid == l) { fastpositions.put(key, b); } }); } return null; } @Override public void paint(Graphics gx) { if(ox < oxp) { oxp -= Math.abs(ox - oxp) * 0.36; } if(ox > oxp) { oxp += Math.abs(oxp - ox) * 0.36; } if(oz < ozp) { ozp -= Math.abs(oz - ozp) * 0.36; } if(oz > ozp) { ozp += Math.abs(ozp - oz) * 0.36; } if(lid != Iris.proj.getCurrentProject().getCacheID()) { working.clear(); workingfast.clear(); positions.clear(); fastpositions.clear(); lid = Iris.proj.getCurrentProject().getCacheID(); Iris.info("Hotloading Vision"); } PrecisionStopwatch p = PrecisionStopwatch.start(); Graphics2D g = (Graphics2D) gx; w = getWidth(); h = getHeight(); double vscale = scale; scale = w / 32D; if(scale != vscale) { positions.clear(); } KSet gg = new KSet<>(); int iscale = (int) scale; g.setColor(Color.white); g.clearRect(0, 0, w, h); posX = (int) oxp; posZ = (int) ozp; m.set(3); for(int r = 0; r < Math.max(w, h); r += iscale) { for(int i = -iscale; i < w + iscale; i += iscale) { for(int j = -iscale; j < h + iscale; j += iscale) { int a = i - (w / 2); int b = j - (h / 2); if(a * a + b * b <= r * r) { BufferedImage t = getTile(gg, iscale, Math.floorDiv((posX / iscale) + i, iscale) * iscale, Math.floorDiv((posZ / iscale) + j, iscale) * iscale, m); if(t != null) { g.drawImage(t, i - ((posX / iscale) % (iscale)), j - ((posZ / iscale) % (iscale)), (int) (iscale), (int) (iscale), new ImageObserver() { @Override public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) { return true; } }); } } } } } p.end(); for(BlockPosition i : positions.k()) { if(!gg.contains(i)) { positions.remove(i); } } if(!isVisible()) { return; } if(!getParent().isVisible()) { return; } if(!getParent().getParent().isVisible()) { return; } J.a(() -> { J.sleep((long) 1); repaint(); }); } private static void createAndShowGUI(Renderer r, short s) { JFrame frame = new JFrame("Vision"); IrisVision nv = new IrisVision(); nv.renderer = new IrisRenderer(r); frame.add(nv); frame.setSize(1440, 820); nv.lid = s; frame.setVisible(true); File file = Iris.getCached("Iris Icon", "https://raw.githubusercontent.com/VolmitSoftware/Iris/master/icon.png"); if(file != null) { try { frame.setIconImage(ImageIO.read(file)); } catch(IOException e) { } } } public static void launch(IrisTerrainProvider g) { J.a(() -> { createAndShowGUI(g.createRenderer(), g.getCacheID()); }); } public void mouseWheelMoved(MouseWheelEvent e) { int notches = e.getWheelRotation(); if(e.isControlDown()) { return; } Iris.info("BPP: " + (mscale) + " BW: " + (w * mscale)); positions.clear(); fastpositions.clear(); mscale = mscale + ((0.044 * mscale) * notches); mscale = mscale < 0.00001 ? 0.00001 : mscale; } }