diff --git a/VoxelEngine/.classpath b/VoxelEngine/.classpath new file mode 100644 index 0000000..fb565a5 --- /dev/null +++ b/VoxelEngine/.classpath @@ -0,0 +1,6 @@ + + + + + + diff --git a/VoxelEngine/.project b/VoxelEngine/.project new file mode 100644 index 0000000..ff36af0 --- /dev/null +++ b/VoxelEngine/.project @@ -0,0 +1,17 @@ + + + VoxelEngine + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/VoxelEngine/.settings/org.eclipse.jdt.core.prefs b/VoxelEngine/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..7341ab1 --- /dev/null +++ b/VoxelEngine/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.7 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.7 diff --git a/VoxelEngine/bin/background.jpg b/VoxelEngine/bin/background.jpg new file mode 100644 index 0000000..1955a05 Binary files /dev/null and b/VoxelEngine/bin/background.jpg differ diff --git a/VoxelEngine/bin/game.au b/VoxelEngine/bin/game.au new file mode 100644 index 0000000..e18f053 Binary files /dev/null and b/VoxelEngine/bin/game.au differ diff --git a/VoxelEngine/bin/java.policy.applet b/VoxelEngine/bin/java.policy.applet new file mode 100644 index 0000000..ba9f51d --- /dev/null +++ b/VoxelEngine/bin/java.policy.applet @@ -0,0 +1,7 @@ +/* AUTOMATICALLY GENERATED ON Tue Apr 16 17:20:59 EDT 2002*/ +/* DO NOT EDIT */ + +grant { + permission java.security.AllPermission; +}; + diff --git a/VoxelEngine/bin/menu.au b/VoxelEngine/bin/menu.au new file mode 100644 index 0000000..6ee23bd Binary files /dev/null and b/VoxelEngine/bin/menu.au differ diff --git a/VoxelEngine/bin/store/Audioapp.class b/VoxelEngine/bin/store/Audioapp.class new file mode 100644 index 0000000..246151f Binary files /dev/null and b/VoxelEngine/bin/store/Audioapp.class differ diff --git a/VoxelEngine/bin/store/SimpleImageLoad.class b/VoxelEngine/bin/store/SimpleImageLoad.class new file mode 100644 index 0000000..1b76c80 Binary files /dev/null and b/VoxelEngine/bin/store/SimpleImageLoad.class differ diff --git a/VoxelEngine/bin/voxelengine/Block.class b/VoxelEngine/bin/voxelengine/Block.class new file mode 100644 index 0000000..c9af665 Binary files /dev/null and b/VoxelEngine/bin/voxelengine/Block.class differ diff --git a/VoxelEngine/bin/voxelengine/BlockLibrary.class b/VoxelEngine/bin/voxelengine/BlockLibrary.class new file mode 100644 index 0000000..227ed66 Binary files /dev/null and b/VoxelEngine/bin/voxelengine/BlockLibrary.class differ diff --git a/VoxelEngine/bin/voxelengine/BlockType.class b/VoxelEngine/bin/voxelengine/BlockType.class new file mode 100644 index 0000000..0f26d6d Binary files /dev/null and b/VoxelEngine/bin/voxelengine/BlockType.class differ diff --git a/VoxelEngine/bin/voxelengine/Camera.class b/VoxelEngine/bin/voxelengine/Camera.class new file mode 100644 index 0000000..bde7ad4 Binary files /dev/null and b/VoxelEngine/bin/voxelengine/Camera.class differ diff --git a/VoxelEngine/bin/voxelengine/Chunk.class b/VoxelEngine/bin/voxelengine/Chunk.class new file mode 100644 index 0000000..3b20f8c Binary files /dev/null and b/VoxelEngine/bin/voxelengine/Chunk.class differ diff --git a/VoxelEngine/bin/voxelengine/Keyboard.class b/VoxelEngine/bin/voxelengine/Keyboard.class new file mode 100644 index 0000000..951665e Binary files /dev/null and b/VoxelEngine/bin/voxelengine/Keyboard.class differ diff --git a/VoxelEngine/bin/voxelengine/Menu.class b/VoxelEngine/bin/voxelengine/Menu.class new file mode 100644 index 0000000..5b7efdb Binary files /dev/null and b/VoxelEngine/bin/voxelengine/Menu.class differ diff --git a/VoxelEngine/bin/voxelengine/Palette.class b/VoxelEngine/bin/voxelengine/Palette.class new file mode 100644 index 0000000..a99233a Binary files /dev/null and b/VoxelEngine/bin/voxelengine/Palette.class differ diff --git a/VoxelEngine/bin/voxelengine/Renderer.class b/VoxelEngine/bin/voxelengine/Renderer.class new file mode 100644 index 0000000..19eff3c Binary files /dev/null and b/VoxelEngine/bin/voxelengine/Renderer.class differ diff --git a/VoxelEngine/bin/voxelengine/SwingInterface$Worker.class b/VoxelEngine/bin/voxelengine/SwingInterface$Worker.class new file mode 100644 index 0000000..699c112 Binary files /dev/null and b/VoxelEngine/bin/voxelengine/SwingInterface$Worker.class differ diff --git a/VoxelEngine/bin/voxelengine/SwingInterface.class b/VoxelEngine/bin/voxelengine/SwingInterface.class new file mode 100644 index 0000000..8de70c7 Binary files /dev/null and b/VoxelEngine/bin/voxelengine/SwingInterface.class differ diff --git a/VoxelEngine/bin/voxelengine/World.class b/VoxelEngine/bin/voxelengine/World.class new file mode 100644 index 0000000..81bce5f Binary files /dev/null and b/VoxelEngine/bin/voxelengine/World.class differ diff --git a/VoxelEngine/src/store/Audioapp.java b/VoxelEngine/src/store/Audioapp.java new file mode 100644 index 0000000..828a91d --- /dev/null +++ b/VoxelEngine/src/store/Audioapp.java @@ -0,0 +1,35 @@ +package store; + +import java.applet.*; +import java.awt.*; +import java.awt.event.*; + +public class Audioapp extends Applet implements ActionListener { + private AudioClip sound; + private Button playSound, loopSound, stopSound; + + public void init() { + System.out.println(getCodeBase()); + sound = getAudioClip(getCodeBase(), "menu.au"); + playSound = new Button("Play"); + playSound.addActionListener(this); + add(playSound); + + loopSound = new Button("Loop"); + loopSound.addActionListener(this); + add(loopSound); + + stopSound = new Button("Stop"); + stopSound.addActionListener(this); + add(stopSound); + } + + public void actionPerformed(ActionEvent e) { + if (e.getSource() == playSound) + sound.play(); + else if (e.getSource() == loopSound) + sound.loop(); + else if (e.getSource() == stopSound) + sound.stop(); + } +} diff --git a/VoxelEngine/src/store/SimpleImageLoad.java b/VoxelEngine/src/store/SimpleImageLoad.java new file mode 100644 index 0000000..14fdb1b --- /dev/null +++ b/VoxelEngine/src/store/SimpleImageLoad.java @@ -0,0 +1,24 @@ +package store; + +import java.awt.*; +import java.applet.*; +import java.net.*; + +public class SimpleImageLoad extends Applet { + Image img; + URL coach; + + public void init() { + try { + coach = new URL("http://www.cs.sbcc.cc.ca.us/~rhd/"); + } catch (MalformedURLException e) { + showStatus("Exception: " + e.toString()); + } + + img = getImage(coach, "coach2c.gif"); + } + + public void paint(Graphics g) { + g.drawImage(img, 0, 0, this); + } +} diff --git a/VoxelEngine/src/voxelengine/Block.java b/VoxelEngine/src/voxelengine/Block.java new file mode 100644 index 0000000..4b92fcc --- /dev/null +++ b/VoxelEngine/src/voxelengine/Block.java @@ -0,0 +1,16 @@ +package voxelengine; + +public class Block { + private BlockType type; + + // constructor + public Block(BlockType type) { + super(); + this.type = type; + } + + // getters + public BlockType getType() { + return type; + } +} diff --git a/VoxelEngine/src/voxelengine/BlockLibrary.java b/VoxelEngine/src/voxelengine/BlockLibrary.java new file mode 100644 index 0000000..1acc282 --- /dev/null +++ b/VoxelEngine/src/voxelengine/BlockLibrary.java @@ -0,0 +1,20 @@ +package voxelengine; + +import java.util.ArrayList; + +public class BlockLibrary { + private ArrayList library = new ArrayList(); + + // methods + public void addType(BlockType type) { + library.add(type); + } + public BlockType getType(String id) { + for (int i = 0; i < library.size(); i++) + if ((library.get(i).getId().equals(id)) || (library.get(i).getName().equals(id))) + return library.get(i); + + // if not found, return null + return null; + } +} diff --git a/VoxelEngine/src/voxelengine/BlockType.java b/VoxelEngine/src/voxelengine/BlockType.java new file mode 100644 index 0000000..1db62b1 --- /dev/null +++ b/VoxelEngine/src/voxelengine/BlockType.java @@ -0,0 +1,34 @@ +package voxelengine; + +import java.awt.Color; +import java.awt.Image; + +public class BlockType { + private Image texture; + private String name; + private String id; + private Color color; + + // constructor + public BlockType(Image texture, String name, String id, Color color) { + super(); + this.texture = texture; + this.name = name; + this.id = id; + this.color = color; + } + + // getters + public Image getTexture() { + return texture; + } + public String getName() { + return name; + } + public String getId() { + return id; + } + public Color getColor() { + return color; + } +} diff --git a/VoxelEngine/src/voxelengine/Camera.java b/VoxelEngine/src/voxelengine/Camera.java new file mode 100644 index 0000000..d02a7de --- /dev/null +++ b/VoxelEngine/src/voxelengine/Camera.java @@ -0,0 +1,23 @@ +package voxelengine; + +public class Camera { + public double x; + public double y; + public double z; + public double rotX; + public double rotY; + public double rotZ; + public double fov; + + // constructor + public Camera(double x, double y, double z, double rotY, double rotZ, double rotX, double fov) { + super(); + this.x = x; + this.y = y; + this.z = z; + this.rotX = rotX; + this.rotY = rotY; + this.rotZ = rotZ; + this.fov = fov; + } +} diff --git a/VoxelEngine/src/voxelengine/Chunk.java b/VoxelEngine/src/voxelengine/Chunk.java new file mode 100644 index 0000000..a397643 --- /dev/null +++ b/VoxelEngine/src/voxelengine/Chunk.java @@ -0,0 +1,62 @@ +package voxelengine; + +public class Chunk { + private static int sz = 16; + private Block[][][] blocks = new Block[sz][sz][sz]; + + // methods + public void setBlock(BlockType type, int x, int y, int z) { + blocks[x][y][z] = new Block(type); + } + public Block getBlock(int x, int y, int z) { + return blocks[x][y][z]; + } + public boolean checkIfEmpty() { + for (int x = 0; x < sz; x++) { + for (int y = 0; y < sz; y++) { + for (int z = 0; z < sz; z++) { + if (blocks[x][y][z] != null) + return false; + } + } + } + return true; + } + + // saving and loading + public String saveChunk() { + // data header + String data = Integer.toString(sz) + "\n"; + + // save data + for (int x = 0; x < sz; x++) { + for (int y = 0; y < sz; y++) { + for (int z = 0; z < sz; z++) { + if (blocks[x][y][z] != null) + data += blocks[x][y][z].getType().getId(); + else + data += "0"; + } + } + } + + return data; + } + public void loadChunk(String data, BlockLibrary lib) throws Exception { + // first check if chunk sizes match + int size = Integer.parseInt(data.substring(0, data.indexOf("\n") - 1)); + if (size != sz) + throw new Exception(); + + // then load data + for (int i = 0; i < data.length(); i++) { + if (data.charAt(i) == '0') + continue; + // calculate current 3d location + int z = (int) (i/(sz*sz)); + int y = (int) ((i - z)/sz); + int x = i - z - y; + blocks[x][y][z] = new Block(lib.getType(String.valueOf(data.charAt(i)))); + } + } +} diff --git a/VoxelEngine/src/voxelengine/Keyboard.java b/VoxelEngine/src/voxelengine/Keyboard.java new file mode 100644 index 0000000..4687a58 --- /dev/null +++ b/VoxelEngine/src/voxelengine/Keyboard.java @@ -0,0 +1,23 @@ +package voxelengine; + +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; + +public class Keyboard implements KeyListener { + private boolean[] keys = new boolean[65536]; + + public void keyTyped(KeyEvent e) { + } + + public void keyPressed(KeyEvent e) { + keys[e.getKeyCode()] = true; + } + + public void keyReleased(KeyEvent e) { + keys[e.getKeyCode()] = false; + } + + public boolean isKeyDown(int key) { + return keys[key]; + } +} diff --git a/VoxelEngine/src/voxelengine/Menu.java b/VoxelEngine/src/voxelengine/Menu.java new file mode 100644 index 0000000..bbbceb2 --- /dev/null +++ b/VoxelEngine/src/voxelengine/Menu.java @@ -0,0 +1,303 @@ +package voxelengine; + +import java.applet.Applet; +import java.applet.AudioClip; +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Choice; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Graphics; +import java.awt.Image; +import java.awt.Label; +import java.awt.Panel; +import java.awt.TextField; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; + +import javax.swing.Box; +import javax.swing.BoxLayout; + +public class Menu extends Applet implements ActionListener, ItemListener { + private Button generateWorld, settings, quit, ok, back, generate; + private TextField size, worldSize, minIt, maxIt, power, zoom, cutoff; + private Label sizeL, colorL, worldSizeL, minItL, maxItL, powerL, zoomL, resL, pixL, castL, cutoffL; + private Choice choice, color; + private AudioClip sound; + private Image background; + private Panel pnl; + + public void init() { + this.setSize(784, 562); + background = getImage(getCodeBase(), "background.jpg"); + sound = getAudioClip(getCodeBase(), "menu.au"); + sound.loop(); + mainMenu(); + } + + public void paint(Graphics g) { + g.drawImage(background, 0, 0, this); + } + + @Override + public void actionPerformed(ActionEvent e) { + Object src = e.getSource(); + if (src == generateWorld) { + clearMenu(); + generationMenu(); + this.setSize(785, 562); + this.setSize(784, 562); + } else if (src == settings) { + clearMenu(); + settingsMenu(); + this.setSize(785, 562); + this.setSize(784, 562); + } else if (src == quit) { + clearMenu(); + System.exit(0); + this.setSize(785, 562); + this.setSize(784, 562); + } else if (src == back) { + clearMenu(); + mainMenu(); + this.setSize(785, 562); + this.setSize(784, 562); + } else if (src == generate) { + sound.stop(); + clearMenu(); + this.setVisible(false); + sound = getAudioClip(getCodeBase(), "game.au"); + sound.loop(); + SwingInterface.main(new String[]{worldSize.getText(), choice.getSelectedItem(), color.getSelectedItem(), size.getText(), minIt.getText(), maxIt.getText(), power.getText(), zoom.getText(), cutoff.getText()}); + } + } + + public void clearMenu() { + this.removeAll(); + repaint(); + } + + public void clearPanel() { + pnl.removeAll(); + repaint(); + } + + public void generationMenu() { + pnl = new Panel(); + pnl.setMaximumSize(new Dimension(350, 200)); + pnl.setPreferredSize(new Dimension(350, 200)); + pnl.setBackground(Color.BLACK); + worldSizeL = new Label("World size: "); + worldSizeL.setForeground(Color.LIGHT_GRAY); + worldSizeL.setBackground(Color.BLACK); + worldSizeL.setMaximumSize(new Dimension(70, 20)); + worldSize = new TextField("20"); + worldSize.setMaximumSize(new Dimension(50, 20)); + choice = new Choice(); + choice.addItem("Menger Sponge"); + choice.addItem("Mandelbulb"); + choice.addItem("Mandelbox"); + choice.setMaximumSize(new Dimension(140, 40)); + choice.setForeground(Color.LIGHT_GRAY); + choice.setBackground(Color.BLACK); + generate = new Button("Generate!"); + generate.setMaximumSize(new Dimension(140, 40)); + generate.setForeground(Color.LIGHT_GRAY); + generate.setBackground(Color.BLACK); + back = new Button("Back"); + back.setMaximumSize(new Dimension(140, 40)); + back.setForeground(Color.LIGHT_GRAY); + back.setBackground(Color.BLACK); + generate.addActionListener(this); + back.addActionListener(this); + choice.addItemListener(this); + + this.setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS)); + + this.add(Box.createRigidArea(new Dimension(0,30))); + add(worldSizeL); + add(worldSize); + this.add(Box.createRigidArea(new Dimension(0,20))); + add(choice); + this.add(Box.createRigidArea(new Dimension(0,30))); + add(pnl); + this.add(Box.createRigidArea(new Dimension(0,50))); + add(generate); + this.add(Box.createRigidArea(new Dimension(0,30))); + add(back); + + colorL = new Label("Color: "); + colorL.setForeground(Color.LIGHT_GRAY); + colorL.setBackground(Color.BLACK); + colorL.setPreferredSize(new Dimension(50, 20)); + color = new Choice(); + color.setPreferredSize(new Dimension(80, 20)); + sizeL = new Label("Size: "); + sizeL.setForeground(Color.LIGHT_GRAY); + sizeL.setBackground(Color.BLACK); + sizeL.setPreferredSize(new Dimension(40, 20)); + size = new TextField("81"); + size.setPreferredSize(new Dimension(50, 20)); + minItL = new Label("Minimum Iterations: "); + minItL.setForeground(Color.LIGHT_GRAY); + minItL.setBackground(Color.BLACK); + minItL.setPreferredSize(new Dimension(120, 20)); + minIt = new TextField("4"); + minIt.setPreferredSize(new Dimension(100, 20)); + maxItL = new Label("Maximum Iterations: "); + maxItL.setForeground(Color.LIGHT_GRAY); + maxItL.setBackground(Color.BLACK); + maxItL.setPreferredSize(new Dimension(120, 20)); + maxIt = new TextField("16"); + maxIt.setPreferredSize(new Dimension(100, 20)); + powerL = new Label("Power: "); + powerL.setForeground(Color.LIGHT_GRAY); + powerL.setBackground(Color.BLACK); + powerL.setPreferredSize(new Dimension(50, 20)); + power = new TextField("8"); + power.setPreferredSize(new Dimension(50, 20)); + zoomL = new Label("Zoom: "); + zoomL.setForeground(Color.LIGHT_GRAY); + zoomL.setBackground(Color.BLACK); + zoomL.setPreferredSize(new Dimension(50, 20)); + zoom = new TextField("4"); + zoom.setPreferredSize(new Dimension(50, 20)); + cutoffL = new Label("Cutoff: "); + cutoffL.setForeground(Color.LIGHT_GRAY); + cutoffL.setBackground(Color.BLACK); + cutoffL.setPreferredSize(new Dimension(50, 20)); + cutoff = new TextField("5"); + cutoff.setPreferredSize(new Dimension(50, 20)); + + pnl.setLayout(new FlowLayout(FlowLayout.LEFT, 10, 10)); + + pnl.add(colorL, BorderLayout.CENTER); + pnl.add(color, BorderLayout.CENTER); + pnl.add(Box.createRigidArea(new Dimension(10,0))); + pnl.add(sizeL, BorderLayout.CENTER); + pnl.add(size, BorderLayout.CENTER); + pnl.add(Box.createRigidArea(new Dimension(50,0))); + pnl.add(powerL, BorderLayout.CENTER); + pnl.add(power, BorderLayout.CENTER); + pnl.add(Box.createRigidArea(new Dimension(50,0))); + pnl.add(zoomL, BorderLayout.CENTER); + pnl.add(zoom, BorderLayout.CENTER); + pnl.add(minItL, BorderLayout.CENTER); + pnl.add(minIt, BorderLayout.CENTER); + pnl.add(Box.createRigidArea(new Dimension(80,0))); + pnl.add(maxItL, BorderLayout.CENTER); + pnl.add(maxIt, BorderLayout.CENTER); + pnl.add(Box.createRigidArea(new Dimension(120,0))); + pnl.add(cutoffL, BorderLayout.CENTER); + pnl.add(cutoff, BorderLayout.CENTER); + + mengerMenu(); + } + + public void mengerMenu() { + colorL.setText("Color: "); + color.removeAll(); + color.addItem("red"); + color.addItem("green"); + color.addItem("blue"); + color.addItem("yellow"); + color.addItem("orange"); + color.addItem("magenta"); + color.addItem("pink"); + color.addItem("cyan"); + color.addItem("black"); + color.addItem("white"); + color.addItem("gray"); + color.addItem("darkGray"); + power.setEnabled(false); + zoom.setEnabled(false); + minIt.setEnabled(false); + maxIt.setEnabled(false); + cutoff.setEnabled(false); + } + + public void bulbMenu() { + colorL.setText("Palette: "); + color.removeAll(); + color.addItem("test"); + color.addItem("blackNblue"); + powerL.setText("Power: "); + power.setEnabled(true); + zoom.setEnabled(false); + minIt.setEnabled(true); + maxIt.setEnabled(true); + cutoff.setEnabled(true); + cutoff.setText("1024"); + } + + public void boxMenu() { + colorL.setText("Palette: "); + color.removeAll(); + color.addItem("test"); + color.addItem("blackNblue"); + powerL.setText("Scale: "); + power.setEnabled(true); + zoom.setEnabled(true); + minIt.setEnabled(true); + maxIt.setEnabled(true); + cutoff.setEnabled(true); + cutoff.setText("5"); + } + + public void settingsMenu() { + back = new Button("Back"); + back.setMaximumSize(new Dimension(140, 40)); + back.setForeground(Color.LIGHT_GRAY); + back.setBackground(Color.BLACK); + back.addActionListener(this); + + this.setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS)); + + this.add(Box.createRigidArea(new Dimension(0,250))); + add(back, BorderLayout.CENTER); + } + + public void mainMenu() { + generateWorld = new Button("Generate World"); + generateWorld.setMaximumSize(new Dimension(140, 40)); + generateWorld.setForeground(Color.LIGHT_GRAY); + generateWorld.setBackground(Color.BLACK); + settings = new Button("Settings"); + settings.setMaximumSize(new Dimension(140, 40)); + settings.setForeground(Color.LIGHT_GRAY); + settings.setBackground(Color.BLACK); + quit = new Button("Quit"); + quit.setMaximumSize(new Dimension(140, 40)); + quit.setForeground(Color.LIGHT_GRAY); + quit.setBackground(Color.BLACK); + generateWorld.addActionListener(this); + settings.addActionListener(this); + quit.addActionListener(this); + + this.setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS)); + + this.add(Box.createRigidArea(new Dimension(0,170))); + add(generateWorld, BorderLayout.CENTER); + this.add(Box.createRigidArea(new Dimension(0,50))); + add(settings, BorderLayout.CENTER); + this.add(Box.createRigidArea(new Dimension(0,50))); + add(quit, BorderLayout.CENTER); + } + + @Override + public void itemStateChanged(ItemEvent arg0) { + if (arg0.getSource() == choice) { + String sel = choice.getSelectedItem(); + if (sel.equals("Menger Sponge")) { + mengerMenu(); + } else if (sel.equals("Mandelbulb")) { + bulbMenu(); + } else if (sel.equals("Mandelbox")){ + boxMenu(); + } + } + } +} diff --git a/VoxelEngine/src/voxelengine/Palette.java b/VoxelEngine/src/voxelengine/Palette.java new file mode 100644 index 0000000..8deb5ec --- /dev/null +++ b/VoxelEngine/src/voxelengine/Palette.java @@ -0,0 +1,25 @@ +package voxelengine; + +public class Palette { + private String name; + private String[] palette; + + // constructor + public Palette(String name, String[] palette) { + super(); + this.name = name; + this.palette = palette; + } + + // methods + public String get(int index) { + return palette[index]; + } + public String getName() { + return name; + } + public void printContents() { + for (int i = 0; i < palette.length; i++) + System.out.println(get(i)); + } +} diff --git a/VoxelEngine/src/voxelengine/Renderer.java b/VoxelEngine/src/voxelengine/Renderer.java new file mode 100644 index 0000000..c4ad9d9 --- /dev/null +++ b/VoxelEngine/src/voxelengine/Renderer.java @@ -0,0 +1,266 @@ +package voxelengine; + +import java.awt.Color; +import java.awt.image.MemoryImageSource; + +public class Renderer { + private World world; + private Color SkyboxColor = Color.BLACK; + private double falloff = 8.0; + private double rate = 0.85; + private int renderDistance = 8; + public Camera camera; + + // constructor + public Renderer(World world, Camera camera) { + super(); + this.world = world; + this.camera = camera; + } + + // rendering methods + public MemoryImageSource render(int width, int height, int pixelScale, int castScale) { + int[] mem = new int[width*height]; + + // raycast for each pixel + for (int py = 0; py < height - pixelScale; py += pixelScale) { + for (int px = 0; px < width - pixelScale; px += pixelScale) { + if (pixelScale == 1) + mem[px + py * width] = raycast(px, py, width, height, castScale); + else { + int val = raycast(px, py, width, height, castScale); + for (int pys = 0; pys < pixelScale; ++pys) { + for (int pxs = 0; pxs < pixelScale; ++pxs) { + mem[px + pxs + (py + pys) * width] = val; + } + } + } + } + } + + // return image source + return new MemoryImageSource(width, height, mem, 0, width); + } + + private int raycast(int px, int py, int width, int height, int castScale) { + double w2 = width/2; + double h2 = height/2; + double yaw = camera.rotY + ((px - w2) / w2) * Math.PI / 4; + double pitch = camera.rotX + ((py - h2) / h2) * Math.PI / 4; + + double x1 = camera.x; + double y1 = camera.y; + double z1 = camera.z; + int x2, y2, z2; + + double xs = Math.sin(pitch) * Math.cos(yaw) / castScale; + double ys = Math.sin(pitch) * Math.sin(yaw) / castScale; + double zs = Math.cos(pitch) / castScale; + + while (inBoundsC(x1, y1, z1)) { + Block block = world.getBlock(x1, y1, z1); + if (block != null) { + Color c = block.getType().getColor(); + c = CalculateColor(c, camera.x, x1, camera.y, y1, camera.z, z1); + return c.getRGB(); + } + x2 = (int) x1; + y2 = (int) y1; + z2 = (int) z1; + do { + x1 += xs; + y1 += ys; + z1 += zs; + } while ((int) x1 == x2 && (int) y1 == y2 && (int) z1 == z2); + } + + return SkyboxColor.getRGB(); + } + + @SuppressWarnings("unused") + private boolean inBounds(double x, double y, double z) { + int b = world.getSize() * 16; + return x >= 0 && x < b && y >= 0 && y < b && z >= 0 && z < b; + } + + public MemoryImageSource renderC(int width, int height, int pixelScale, int castScale) { + int[] mem = new int[width*height]; + + // raycast for each pixel + for (int py = 0; py < height - pixelScale; py += pixelScale) { + for (int px = 0; px < width - pixelScale; px += pixelScale) { + if (pixelScale == 1) + mem[px + py * width] = raycastC(px, py, width, height, castScale); + else { + int val = raycastC(px, py, width, height, castScale); + for (int pys = 0; pys < pixelScale; ++pys) { + for (int pxs = 0; pxs < pixelScale; ++pxs) { + mem[px + pxs + (py + pys) * width] = val; + } + } + } + } + } + + // return image source + return new MemoryImageSource(width, height, mem, 0, width); + } + + private int raycastC(int px, int py, int width, int height, int castScale) { + double w2 = width/2; + double h2 = height/2; + double yaw = camera.rotY + ((px - w2) / w2) * Math.PI / 4; + double pitch = camera.rotX + ((py - h2) / h2) * Math.PI / 4; + + double x1 = camera.x; + double y1 = camera.y; + double z1 = camera.z; + int x2, y2, z2; + int cx1, cy1, cz1, cx2, cy2, cz2; + + double xs = Math.sin(pitch) * Math.cos(yaw) / castScale; + double ys = Math.sin(pitch) * Math.sin(yaw) / castScale; + double zs = Math.cos(pitch) / castScale; + + while (inBoundsC(x1, y1, z1)) { + // chunk processing + cx1 = (int) (x1/16.0); + cy1 = (int) (y1/16.0); + cz1 = (int) (z1/16.0); + if (world.getIsEmpty(cx1, cy1, cz1)) { + do { + x1 += xs; + y1 += ys; + z1 += zs; + cx2 = (int) (x1/16.0); + cy2 = (int) (y1/16.0); + cz2 = (int) (z1/16.0); + } while (cx1 == cx2 && cy1 == cy2 && cz1 == cz2); + continue; + } + + // block processing + Block block = world.getBlock(x1, y1, z1); + if (block != null) { + Color c = block.getType().getColor(); + c = CalculateColor(c, camera.x, x1, camera.y, y1, camera.z, z1); + return c.getRGB(); + } + + x2 = (int) x1; + y2 = (int) y1; + z2 = (int) z1; + do { + x1 += xs; + y1 += ys; + z1 += zs; + } while ((int) x1 == x2 && (int) y1 == y2 && (int) z1 == z2); + } + + return SkyboxColor.getRGB(); + } + + private boolean inBoundsC(double x, double y, double z) { + int b = world.getSize() * 16; + int dx = ((int) Math.abs(camera.x - x)) / 16; + int dy = ((int) Math.abs(camera.y - y)) / 16; + int dz = ((int) Math.abs(camera.z - z)) / 16; + return x >= 0 && x < b && y >= 0 && y < b && z >= 0 && z < b && dx < renderDistance && dy < renderDistance && dz < renderDistance; + } + + public MemoryImageSource renderD(int width, int height, int pixelScale, int castScale) { + int[] mem = new int[width*height]; + + // raycast for each pixel + for (int py = 0; py < height - pixelScale; py += pixelScale) { + for (int px = 0; px < width - pixelScale; px += pixelScale) { + if (pixelScale == 1) + mem[px + py * width] = raycastD(px, py, width, height, castScale); + else { + int val = raycastD(px, py, width, height, castScale); + for (int pys = 0; pys < pixelScale; ++pys) { + for (int pxs = 0; pxs < pixelScale; ++pxs) { + mem[px + pxs + (py + pys) * width] = val; + } + } + } + } + } + + // return image source + return new MemoryImageSource(width, height, mem, 0, width); + } + + private int raycastD(int px, int py, int width, int height, int castScale) { + double w2 = width/2; + double h2 = height/2; + double yaw = camera.rotY + ((px - w2) / w2) * Math.PI / 4; + double pitch = camera.rotX + ((py - h2) / h2) * Math.PI / 4; + yaw = (pitch < 0 || pitch > Math.PI) ? (yaw + Math.PI) % (2*Math.PI): yaw; + pitch = (pitch < 0) ? Math.abs(pitch) : pitch; + pitch = (pitch > Math.PI) ? 2*Math.PI - pitch : pitch; + + double x1 = camera.x; + double y1 = camera.y; + double z1 = camera.z; + int x2, y2, z2; + int cx1, cy1, cz1, cx2, cy2, cz2; + + double xs = Math.sin(pitch) * Math.cos(yaw) / castScale; + double ys = Math.sin(pitch) * Math.sin(yaw) / castScale; + double zs = Math.cos(pitch) / castScale; + + while (inBoundsC(x1, y1, z1)) { + // chunk processing + cx1 = (int) (x1/16.0); + cy1 = (int) (y1/16.0); + cz1 = (int) (z1/16.0); + if (world.getIsEmpty(cx1, cy1, cz1)) { + do { + x1 += xs; + y1 += ys; + z1 += zs; + cx2 = (int) (x1/16.0); + cy2 = (int) (y1/16.0); + cz2 = (int) (z1/16.0); + } while (cx1 == cx2 && cy1 == cy2 && cz1 == cz2); + continue; + } + + // block processing + Block block = world.getBlock(x1, y1, z1); + if (block != null) { + Color c = block.getType().getColor(); + c = CalculateColor(c, camera.x, x1, camera.y, y1, camera.z, z1); + return c.getRGB(); + } + + x2 = (int) x1; + y2 = (int) y1; + z2 = (int) z1; + do { + x1 += xs; + y1 += ys; + z1 += zs; + } while ((int) x1 == x2 && (int) y1 == y2 && (int) z1 == z2); + } + + return SkyboxColor.getRGB(); + } + + private Color CalculateColor(Color c, double x1, double x2, double y1, double y2, double z1, double z2) { + double distance = Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2) + Math.pow(z1 - z2, 2)); + double lightIntensity = falloff/Math.pow(distance, rate); + + // calculate color components + int red = (int) (c.getRed() * lightIntensity); + red = (red > 255) ? 255 : red; + int green = (int) (c.getGreen() * lightIntensity); + green = (green > 255) ? 255 : green; + int blue = (int) (c.getBlue() * lightIntensity); + blue = (blue > 255) ? 255 : blue; + + // return calculated color + return new Color(red, green, blue); + } +} diff --git a/VoxelEngine/src/voxelengine/SwingInterface.java b/VoxelEngine/src/voxelengine/SwingInterface.java new file mode 100644 index 0000000..732cc66 --- /dev/null +++ b/VoxelEngine/src/voxelengine/SwingInterface.java @@ -0,0 +1,165 @@ +package voxelengine; + +import java.applet.AudioClip; +import java.awt.BorderLayout; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.image.BufferedImage; +import java.util.List; + +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.SwingWorker; + +@SuppressWarnings("serial") +public class SwingInterface extends JPanel { + private static Keyboard keyboard = new Keyboard(); + private Image src = null; + private World world; + private Renderer renderer; + private Camera camera = new Camera(0, 0, 0, 0, 0, 0, Math.PI/2); + private static String[] arguments; + + private static final int X_SIZE = 800; + private static final int Y_SIZE = 600; + private static int pixelScale = 4; + private static int castScale = 4; + + public SwingInterface() { + new Worker().execute(); + } + + protected void paintComponent(Graphics g) { + super.paintComponent(g); + if (src != null) + g.drawImage(src, 0, 0, this); + } + + private class Worker extends SwingWorker { + + protected void process(List chunks) { + for (Image bufferedImage : chunks) { + src = bufferedImage; + repaint(); + } + } + + protected Void doInBackground() throws Exception { + set_up(); + int frames = 0; + long time = System.currentTimeMillis(); + + while (true) { + frames++; + if (frames == 30) { + double t = System.currentTimeMillis() - time; + double fps = 30 / (t / 1000); + System.out.println(((double) Math.round(fps * 100)/100) + "fps"); + frames = 0; + time = System.currentTimeMillis(); + } + process_input(); + Image img = createImage(renderer.renderC(X_SIZE, Y_SIZE, pixelScale, castScale)); + BufferedImage buffer = new BufferedImage(X_SIZE, Y_SIZE, BufferedImage.TYPE_INT_ARGB); + Graphics2D g2 = buffer.createGraphics(); + g2.drawImage(img, 0, 0, null); + g2.dispose(); + publish(buffer); + } + } + + private void set_up() { + int worldSize = Integer.parseInt(arguments[0]); + int offset = (worldSize * 16 - Integer.parseInt(arguments[3]))/2; + world = new World(worldSize); + world.addColorBlockTypes(); + world.addPalettes(); + long time = System.currentTimeMillis(); + if (arguments[1].equals("Menger Sponge")) { + System.out.println("Generating Menger Sponge..."); + world.generateMengerSponge(arguments[2], Integer.parseInt(arguments[3]), offset, offset, offset); + } else if (arguments[1].equals("Mandelbulb")) { + System.out.println("Generating Mandelbulb..."); + world.generateMandelbulb(arguments[2], Integer.parseInt(arguments[3]), Double.parseDouble(arguments[6]), Double.parseDouble(arguments[8]), offset, offset, offset, Integer.parseInt(arguments[4]), Integer.parseInt(arguments[5])); + } else if (arguments[1].equals("Mandelbox")) { + System.out.println("Generating Mandelbox..."); + world.generateMandelbox(arguments[2], Integer.parseInt(arguments[3]), Double.parseDouble(arguments[6]), Double.parseDouble(arguments[8]), Integer.parseInt(arguments[4]), Integer.parseInt(arguments[5]), Double.parseDouble(arguments[7]), offset, offset, offset); + } + System.out.println((Math.round((System.currentTimeMillis() - time)/10)/100.0)+" seconds spent generating world."); + time = System.currentTimeMillis(); + world.updateIsEmptyAll(); + System.out.println((Math.round((System.currentTimeMillis() - time)/10)/100.0)+" seconds spent updating isEmpty booleans."); + camera.x = (double) (offset - 2.0); + camera.y = (double) (offset - 2.0); + camera.z = (double) (offset - 2.0); + camera.rotY = 0; + camera.rotX = Math.PI / 2; + renderer = new Renderer(world, camera); + System.out.println("World set up. Now rendering..."); + } + + private void process_input() { + if (keyboard.isKeyDown('A')) { + camera.x += Math.cos(camera.rotY - Math.PI / 2); + camera.y += Math.sin(camera.rotY - Math.PI / 2); + } + if (keyboard.isKeyDown('D')) { + camera.x += Math.cos(camera.rotY + Math.PI / 2); + camera.y += Math.sin(camera.rotY + Math.PI / 2); + } + if (keyboard.isKeyDown('W')) { + camera.x += Math.cos(camera.rotY); + camera.y += Math.sin(camera.rotY); + } + if (keyboard.isKeyDown('S')) { + camera.x += Math.cos(camera.rotY + Math.PI); + camera.y += Math.sin(camera.rotY + Math.PI); + } + if (keyboard.isKeyDown('Q')) { + camera.z -= 1; + } + if (keyboard.isKeyDown('E')) { + camera.z += 1; + } + if (keyboard.isKeyDown('J')) { + camera.rotY -= Math.PI / 32; + } + if (keyboard.isKeyDown('L')) { + camera.rotY += Math.PI / 32; + } + if (keyboard.isKeyDown('I')) { + camera.rotX -= Math.PI / 32; + } + if (keyboard.isKeyDown('K')) { + camera.rotX += Math.PI / 32; + } + if (keyboard.isKeyDown('T')) { + pixelScale++; + } + if (keyboard.isKeyDown('Y')) { + pixelScale--; + } + if (keyboard.isKeyDown('G')) { + castScale++; + } + if (keyboard.isKeyDown('H')) { + castScale--; + } + camera.rotY = camera.rotY % (Math.PI * 2); + camera.rotX = Math.max(Math.min(camera.rotX, Math.PI), 0); + pixelScale = Math.max(Math.min(pixelScale, 10), 1); + castScale = Math.max(Math.min(castScale, 50), 1); + } + } + + public static void main(String[] args) { + arguments = args; + JFrame jf = new JFrame(); + jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + jf.addKeyListener(keyboard); + jf.getContentPane().add(new SwingInterface(), BorderLayout.CENTER); + jf.setSize(X_SIZE, Y_SIZE); + jf.setVisible(true); + } +} diff --git a/VoxelEngine/src/voxelengine/World.java b/VoxelEngine/src/voxelengine/World.java new file mode 100644 index 0000000..d058af4 --- /dev/null +++ b/VoxelEngine/src/voxelengine/World.java @@ -0,0 +1,353 @@ +package voxelengine; + +import java.awt.Color; +import java.util.ArrayList; +import java.util.Random; + +public class World { + private int size; + private Chunk[][][] chunks; + private boolean[][][] isEmpty; + private BlockLibrary library = new BlockLibrary(); + private ArrayList palettes = new ArrayList(); + + // constructor + public World(int size) { + super(); + this.size = size; + this.chunks = new Chunk[size][size][size]; + this.isEmpty = new boolean[size][size][size]; + + for (int x = 0; x < size; x++) + for (int y = 0; y < size; y++) + for (int z = 0; z < size; z++) { + this.chunks[x][y][z] = new Chunk(); + this.isEmpty[x][y][z] = false; + } + } + + // methods + public int getSize() { + return size; + } + + // chunk/block methods + public Chunk getChunk(int x, int y, int z) { + if ((x < size && x >= 0) && (y < size && y >= 0) && (z < size && z >= 0)) + return chunks[x][y][z]; + else + return null; + } + public Block getBlock(int x, int y, int z) { + int xChunk = x/16; + int yChunk = y/16; + int zChunk = z/16; + Chunk chunk = getChunk(xChunk, yChunk, zChunk); + if (chunk != null) + return chunk.getBlock(x % 16, y % 16, z % 16); + else + return null; + } + public Block getBlock(double x, double y, double z) { + int xi = (int) x; + int yi = (int) y; + int zi = (int) z; + return getBlock(xi, yi, zi); + } + public void setBlock(String id, int x, int y, int z) { + int xChunk = x/16; + int yChunk = y/16; + int zChunk = z/16; + Chunk chunk = getChunk(xChunk, yChunk, zChunk); + BlockType type = getType(id); + chunk.setBlock(type, x % 16, y % 16, z % 16); + } + public void setBlock(String id, double x, double y, double z) { + int xi = (int) x; + int yi = (int) y; + int zi = (int) z; + setBlock(id, xi, yi, zi); + } + + // isEmpty methods + public boolean getIsEmpty(int x, int y, int z) { + return isEmpty[x][y][z]; + } + public void updateIsEmpty(int x, int y, int z) { + Chunk chunk = getChunk(x, y, z); + isEmpty[x][y][z] = chunk.checkIfEmpty(); + } + public void updateIsEmptyAll() { + for (int x = 0; x < size; x++) + for (int y = 0; y < size; y++) + for (int z = 0; z < size; z++) + isEmpty[x][y][z] = getChunk(x,y,z).checkIfEmpty(); + } + + // block type methods + public void addType(BlockType type) { + library.addType(type); + } + public BlockType getType(String id) { + return library.getType(id); + } + + // world generation methods + public void generateMengerSponge(String id, int d0, int xOffset, int yOffset, int zOffset) { + // place block and return if dimension = 1 + if (d0 == 1) { + setBlock(id, xOffset, yOffset, zOffset); + return; + } + + // 1/3 of dimension + int d1 = d0/3; + + // recursion loop + for (int x = 0; x < 3; x++) { + for (int y = 0; y < 3; y++) { + if ((y == 1) && (x == 1)) + continue; + for (int z = 0; z < 3; z++) { + if (((y == 1) && (z == 1)) || ((x == 1) && (z == 1))) + continue; + // recursion + if (d0 > 3) + generateMengerSponge(id, d1, x*d1 + xOffset, y*d1 + yOffset, z*d1 + zOffset); + else { + setBlock(id, x + xOffset, y + yOffset, z + zOffset); + } + } + } + } + } + + public void generateBox(String id, int d0, int xOffset, int yOffset, int zOffset) { + for (int x = 0; x < d0; x++) { + for (int y = 0; y < d0; y++) { + setBlock(id, x + xOffset, y + yOffset, zOffset); + setBlock(id, x + xOffset, y + yOffset, (d0 - 1) + zOffset); + } + } + for (int y = 0; y < d0; y++) { + for (int z = 0; z < d0; z++) { + setBlock(id, xOffset, y + yOffset, z + zOffset); + setBlock(id, (d0 - 1) + xOffset, y + yOffset, z + zOffset); + } + } + for (int x = 0; x < d0; x++) { + for (int z = 0; z < d0; z++) { + setBlock(id, x + xOffset, yOffset, z + zOffset); + setBlock(id, x + xOffset, (d0 - 1) + yOffset, z + zOffset); + } + } + } + + public void generateBensWorld() { + for (int i = 0; i < 100; ++i) { + Random r = new Random(i); + for (int j = 0; j < 100; ++j) { + setBlock("blue", i, j, 0); + if ((i + j) % 2 == 0) + setBlock("red", i, 0, j); + else + setBlock("black", i, 0, j); + setBlock("orange", 0, i, j); + setBlock("cyan", i, j, 50); + setBlock("yellow", i, 99, j); + setBlock("pink", 99, i, j); + + if (i % 4 == 0) { + int rand = r.nextInt(100); + if (rand < 10) + setBlock("green", i, 70, j); + else if (rand < 50) + setBlock("gray", i, 70, j); + else + setBlock("darkGray", i, 70, j); + } else if (i % 2 == 0) + setBlock("black", i, 70, j); + } + } + } + + // generate mandelbulb + public void generateMandelbulb(String name, int d, double power, double cutoff, int xOffset, int yOffset, int zOffset, int itMin, int itMax) { + Palette palette = getPalette(name); + //palette.printContents(); + for (int x = 0; x < d; x++) { + double xc = tf(x, d); + for (int y = 0; y < d; y++) { + double yc = tf(y, d); + for (int z = 0; z < d; z++) { + double zc = tf(z, d); + + int iterations = -1; + double[] C = new double[]{xc, yc, zc}; + double[] Z = new double[]{0, 0, 0}; + + // iterate over vectors + while ((mag(Z) <= cutoff) && (iterations < itMax)) { + Z = add(formula(Z, power), C); + iterations++; + } + + // place block if cutoff reached in itMin -> itMax range + if ((iterations >= itMin) && (iterations < itMax)) { + String id = palette.get(iterations - itMin); + //System.out.println("Placing block "+id+" at ("+(xOffset + x)+","+(yOffset + y)+","+(zOffset + z)+")"); + setBlock(id, xOffset + x, yOffset + y, zOffset + z); + } + } + } + } + } + + private double tf(int v, int d) { + return (2.0 * v)/((double) d) - 1.0; + } + + private double mag(double[] vec) { + return Math.sqrt(vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2]); + } + + private double[] add(double[] vec1, double[] vec2) { + return new double[]{vec1[0] + vec2[0], vec1[1] + vec2[1], vec1[2] + vec2[2]}; + } + + // mandelbulb vector growth formula + // if current vector magnitude > 1 and t is not too close to a multiple of (pi/2) + // this will give a vector of greater magnitude than the vector put in + // n, which is param, is the rate at which vector magnitude grows. + private double[] formula(double[] vec, double n) { + double t = theta(vec); + double p = phi(vec); + double k = Math.pow(mag(vec), n); + return new double[] { + k*Math.sin(n*t)*Math.cos(n*p), + k*Math.sin(n*t)*Math.sin(n*p), + k*Math.cos(n*t)}; + } + + // theta vector value (arccos) + private double theta(double[] vec) { + return (Math.acos(vec[2]/(mag(vec) + 0.000000001))); + } + + // phi vector value (arctan) + private double phi(double[] vec) { + return (Math.atan(vec[1]/(vec[0] + 0.000000001))); + } + + // generate mandelbox + public void generateMandelbox(String name, int d, double scale, double cutoff, int itMin, int itMax, double tfmult, int xOffset, int yOffset, int zOffset) { + double tfsub = tfmult/2; + Palette palette = getPalette(name); + for (int x = 0; x < d; x++) { + double xc = (tfmult * x)/(d - 1) - tfsub; + for (int y = 0; y < d; y++) { + double yc = (tfmult * y)/(d - 1) - tfsub; + for (int z = 0; z < d; z++) { + double zc = (tfmult * z)/(d - 1) - tfsub; + + int iterations = -1; + double[] C = new double[]{xc, yc, zc}; + double[] Z = new double[]{0, 0, 0}; + + while ((mag(Z) < cutoff) && (iterations < itMax)) { + Z = add(mult(scale, boxformula(Z)), C); + iterations++; + } + + if ((iterations >= itMin) && (iterations < itMax)) { + // place block if cutoff reached in itMin -> itMax range + if ((iterations >= itMin) && (iterations < itMax)) { + String id = palette.get(iterations - itMin); + //System.out.println("Placing block "+id+" at ("+(xOffset + x)+","+(yOffset + y)+","+(zOffset + z)+")"); + setBlock(id, xOffset + x, yOffset + y, zOffset + z); + } + } + } + } + } + } + + // mandelbox vector growth formula + private double[] boxformula(double[] vec) { + double x = vec[0]; + double y = vec[1]; + double z = vec[2]; + + if (x > 1) + x = 2 - x; + else if (x < -1) + x = -2 - x; + + if (y > 1) + y = 2 - y; + else if (y < -1) + y = -2 - y; + + if (z > 1) + z = 2 - z; + else if (z < -1) + z = -2 - z; + + double[] output = new double[]{x, y, z}; + double m = mag(output); + + if (m < 0.5) + output = mult(4, output); + else if (m < 1) + output = mult(1/(m*m), output); + + return output; + } + + // multiplies a constant and a vector together + private double[] mult(double c, double[] vec) { + double[] output = new double[]{ + c * vec[0], + c * vec[1], + c * vec[2]}; + + return output; + } + + // palettes and block types + public void addColorBlockTypes() { + library.addType(new BlockType(null, "red", "1", Color.RED)); + library.addType(new BlockType(null, "green", "2", Color.GREEN)); + library.addType(new BlockType(null, "blue", "3", Color.BLUE)); + library.addType(new BlockType(null, "yellow", "4", Color.YELLOW)); + library.addType(new BlockType(null, "orange", "5", Color.ORANGE)); + library.addType(new BlockType(null, "magenta", "6", Color.MAGENTA)); + library.addType(new BlockType(null, "pink", "7", Color.PINK)); + library.addType(new BlockType(null, "cyan", "8", Color.CYAN)); + library.addType(new BlockType(null, "black", "9", Color.BLACK)); + library.addType(new BlockType(null, "white", "A", Color.WHITE)); + library.addType(new BlockType(null, "gray", "B", Color.GRAY)); + library.addType(new BlockType(null, "darkGray", "C", Color.DARK_GRAY)); + } + + public void addPalettes() { + palettes.add(new Palette("test", new String[]{"white", "gray", "darkGray", "black", "red", "orange", "yellow", "green", "cyan", "blue", "magenta", "pink"})); + palettes.add(new Palette("blackNblue", new String[]{"white", "gray", "darkGray", "black", "blue", "cyan", "white", "gray", "darkGray", "blue", "cyan", "white"})); + } + + public Palette getPalette(String name) { + for (int i = 0; i < palettes.size(); i++) { + if (palettes.get(i).getName().equals(name)) + return palettes.get(i); + } + return null; + } + + // load/save world + public void load(String filename) { + + } + public void save(String filename) { + + } +}