├── .gitattributes ├── .gitignore ├── README.md ├── cow.obj ├── src ├── Box.java ├── Main.java └── njine │ ├── assets │ ├── AssetLoader.java │ ├── AssetManager.java │ ├── OBJLoader.java │ └── TextureLoader.java │ ├── camera │ ├── Camera.java │ └── FPCam.java │ ├── entity │ ├── Entity.java │ ├── Material.java │ ├── Mesh.java │ ├── Model.java │ └── Transform.java │ ├── math │ ├── Mat44.java │ ├── MatBuilder.java │ ├── Vec2.java │ └── Vec3.java │ ├── render │ ├── RenderSurface.java │ ├── Renderer.java │ ├── RendererThread.java │ └── VertexAttribs.java │ ├── util │ └── Input.java │ └── world │ ├── AppConfig.java │ └── World.java └── teapot.obj /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Eclipse Stuff 2 | bin/ 3 | .settings/ 4 | .project 5 | .classpath 6 | 7 | *.class 8 | 9 | # Mobile Tools for Java (J2ME) 10 | .mtj.tmp/ 11 | 12 | # Package Files # 13 | *.jar 14 | *.war 15 | *.ear 16 | 17 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 18 | hs_err_pid* 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear in the root of a volume 35 | .DocumentRevisions-V100 36 | .fseventsd 37 | .Spotlight-V100 38 | .TemporaryItems 39 | .Trashes 40 | .VolumeIcon.icns 41 | 42 | # Directories potentially created on remote AFP share 43 | .AppleDB 44 | .AppleDesktop 45 | Network Trash Folder 46 | Temporary Items 47 | .apdisk 48 | 49 | # Windows 50 | # ========================= 51 | 52 | # Windows image file caches 53 | Thumbs.db 54 | ehthumbs.db 55 | 56 | # Folder config file 57 | Desktop.ini 58 | 59 | # Recycle Bin used on file shares 60 | $RECYCLE.BIN/ 61 | 62 | # Windows Installer files 63 | *.cab 64 | *.msi 65 | *.msm 66 | *.msp 67 | 68 | # Windows shortcuts 69 | *.lnk 70 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # java-3d-renderer 2 | simple pure software renderer written in Java using the standard 2D graphics libraries 3 | -------------------------------------------------------------------------------- /src/Box.java: -------------------------------------------------------------------------------- 1 | import njine.entity.Entity; 2 | import njine.entity.Model; 3 | import njine.entity.Transform; 4 | import njine.math.Vec3; 5 | 6 | 7 | public class Box extends Entity 8 | { 9 | public void setup() 10 | { 11 | 12 | } 13 | 14 | public void update() 15 | { 16 | getTransform().setLocalRotation(new Vec3(0, getTransform().getLocalRotation().y+2, 0)); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Main.java: -------------------------------------------------------------------------------- 1 | import java.io.FileNotFoundException; 2 | import java.io.IOException; 3 | 4 | import njine.assets.AssetManager; 5 | import njine.camera.Camera; 6 | import njine.camera.FPCam; 7 | import njine.entity.Mesh; 8 | import njine.entity.Model; 9 | import njine.math.Vec3; 10 | import njine.render.RenderSurface; 11 | import njine.render.Renderer; 12 | import njine.world.AppConfig; 13 | import njine.world.World; 14 | 15 | 16 | public class Main 17 | { 18 | public static void main(String[] args) 19 | { 20 | /* commenting out a comment 21 | Vec3[] verts = {new Vec3(-1, -1, 1), 22 | new Vec3(-1, -1, -1), 23 | new Vec3(-1, 1, 1), 24 | new Vec3(-1, 1, -1), 25 | new Vec3(1, -1, 1), 26 | new Vec3(1, -1, -1), 27 | new Vec3(1, 1, 1), 28 | new Vec3(1, 1, -1)}; 29 | 30 | int[] tris = {0, 1, 5, 0, 5, 4, 31 | 0, 1, 2, 1, 3, 2, 32 | 1, 5, 7, 1, 3, 7}; 33 | */ 34 | 35 | /* 36 | Vec3 verts[] = { 37 | new Vec3( -2.5703, 0.78053, -2.4e-05), new Vec3( -0.89264, 0.022582, 0.018577), 38 | new Vec3( 1.6878, -0.017131, 0.022032), new Vec3( 3.4659, 0.025667, 0.018577), 39 | new Vec3( -2.5703, 0.78969, -0.001202), new Vec3( -0.89264, 0.25121, 0.93573), 40 | new Vec3( 1.6878, 0.25121, 1.1097), new Vec3( 3.5031, 0.25293, 0.93573), 41 | new Vec3( -2.5703, 1.0558, -0.001347), new Vec3( -0.89264, 1.0558, 1.0487), 42 | new Vec3( 1.6878, 1.0558, 1.2437), new Vec3( 3.6342, 1.0527, 1.0487), 43 | new Vec3( -2.5703, 1.0558, 0), new Vec3( -0.89264, 1.0558, 0), 44 | new Vec3( 1.6878, 1.0558, 0), new Vec3( 3.6342, 1.0527, 0), 45 | new Vec3( -2.5703, 1.0558, 0.001347), new Vec3( -0.89264, 1.0558, -1.0487), 46 | new Vec3( 1.6878, 1.0558, -1.2437), new Vec3( 3.6342, 1.0527, -1.0487), 47 | new Vec3( -2.5703, 0.78969, 0.001202), new Vec3( -0.89264, 0.25121, -0.93573), 48 | new Vec3( 1.6878, 0.25121, -1.1097), new Vec3( 3.5031, 0.25293, -0.93573), 49 | new Vec3( 3.5031, 0.25293, 0), new Vec3( -2.5703, 0.78969, 0), 50 | new Vec3( 1.1091, 1.2179, 0), new Vec3( 1.145, 6.617, 0), 51 | new Vec3( 4.0878, 1.2383, 0), new Vec3( -2.5693, 1.1771, -0.081683), 52 | new Vec3( 0.98353, 6.4948, -0.081683), new Vec3( -0.72112, 1.1364, -0.081683), 53 | new Vec3( 0.9297, 6.454, 0), new Vec3( -0.7929, 1.279, 0), 54 | new Vec3( 0.91176, 1.2994, 0)}; 55 | 56 | int tris[] = { 57 | 4, 0, 5, 0, 1, 5, 1, 2, 5, 5, 2, 6, 3, 7, 2, 58 | 2, 7, 6, 5, 9, 4, 4, 9, 8, 5, 6, 9, 9, 6, 10, 59 | 7, 11, 6, 6, 11, 10, 9, 13, 8, 8, 13, 12, 10, 14, 9, 60 | 9, 14, 13, 10, 11, 14, 14, 11, 15, 17, 16, 13, 12, 13, 16, 61 | 13, 14, 17, 17, 14, 18, 15, 19, 14, 14, 19, 18, 16, 17, 20, 62 | 20, 17, 21, 18, 22, 17, 17, 22, 21, 18, 19, 22, 22, 19, 23, 63 | 20, 21, 0, 21, 1, 0, 22, 2, 21, 21, 2, 1, 22, 23, 2, 64 | 2, 23, 3, 3, 23, 24, 3, 24, 7, 24, 23, 15, 15, 23, 19, 65 | 24, 15, 7, 7, 15, 11, 0, 25, 20, 0, 4, 25, 20, 25, 16, 66 | 16, 25, 12, 25, 4, 12, 12, 4, 8, 26, 27, 28, 29, 30, 31, 67 | 32, 34, 33 }; 68 | */ 69 | 70 | Mesh m = null; 71 | 72 | try { 73 | m = (Mesh) AssetManager.loadAsset("teapot.obj"); 74 | } catch (FileNotFoundException e) { 75 | // TODO Auto-generated catch block 76 | e.printStackTrace(); 77 | } catch (IOException e) { 78 | // TODO Auto-generated catch block 79 | e.printStackTrace(); 80 | } 81 | 82 | Box b = new Box(); 83 | //b.getModel().setMesh(new Mesh(verts, tris)); 84 | b.getModel().setMesh(m); 85 | b.getTransform().setGlobalPosition(new Vec3(0, 0, 0)); 86 | 87 | Camera cam = new FPCam(); 88 | cam.getTransform().setGlobalPosition(new Vec3(0, 2, 8)); 89 | cam.getTransform().setLocalRotation(new Vec3(20, 0, 0)); 90 | 91 | AppConfig config = new AppConfig(); 92 | World world = new World(config); 93 | world.addEntity(b); 94 | world.setCamera(cam); 95 | world.begin(); 96 | } 97 | } 98 | 99 | -------------------------------------------------------------------------------- /src/njine/assets/AssetLoader.java: -------------------------------------------------------------------------------- 1 | package njine.assets; 2 | 3 | import java.io.FileNotFoundException; 4 | import java.io.IOException; 5 | 6 | public interface AssetLoader { 7 | 8 | public Object load(String filename) throws FileNotFoundException, IOException; 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/njine/assets/AssetManager.java: -------------------------------------------------------------------------------- 1 | package njine.assets; 2 | 3 | import java.io.FileNotFoundException; 4 | import java.io.IOException; 5 | import java.util.HashMap; 6 | 7 | public class AssetManager 8 | { 9 | private static HashMap loaders = defaultLoaders(); 10 | 11 | private AssetManager(){} 12 | 13 | public static HashMap defaultLoaders() 14 | { 15 | HashMap loaders = new HashMap(); 16 | loaders.put("obj", new OBJLoader()); 17 | loaders.put("jpg", new TextureLoader()); 18 | loaders.put("jpeg", new TextureLoader()); 19 | loaders.put("png", new TextureLoader()); 20 | loaders.put("gif", new TextureLoader()); 21 | loaders.put("bmp", new TextureLoader()); 22 | loaders.put("wbmp", new TextureLoader()); 23 | return loaders; 24 | } 25 | 26 | public static void addAssetLoader(String type, AssetLoader loader) 27 | { 28 | loaders.put(type.toLowerCase(), loader); 29 | } 30 | 31 | public static void removeAssetLoader(String type) 32 | { 33 | loaders.remove(type.toLowerCase()); 34 | } 35 | 36 | public static Object loadAsset(String filename) throws FileNotFoundException, IOException 37 | { 38 | String[] split = filename.split("\\."); 39 | String end = split[split.length-1].toLowerCase(); 40 | if(!loaders.containsKey(end)) 41 | throw new IllegalArgumentException("asset of type " + end + " not supported"); 42 | AssetLoader loader = loaders.get(end); 43 | return loader.load(filename); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/njine/assets/OBJLoader.java: -------------------------------------------------------------------------------- 1 | package njine.assets; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.FileNotFoundException; 5 | import java.io.FileReader; 6 | import java.io.IOException; 7 | import java.util.ArrayList; 8 | 9 | import njine.entity.Material; 10 | import njine.entity.Mesh; 11 | import njine.entity.Model; 12 | import njine.math.Vec3; 13 | 14 | public class OBJLoader implements AssetLoader 15 | { 16 | 17 | public Object load(String filename) throws FileNotFoundException, IOException 18 | { 19 | BufferedReader br = new BufferedReader(new FileReader(filename)); 20 | ArrayList verts = new ArrayList(); 21 | ArrayList tris = new ArrayList(); 22 | 23 | String line = br.readLine(); 24 | while(line != null) 25 | { 26 | String[] words = line.split(" "); 27 | if (words.length != 0) 28 | { 29 | switch(words[0]) 30 | { 31 | case "v": 32 | double x = Double.parseDouble(words[1]); 33 | double y = Double.parseDouble(words[2]); 34 | double z = Double.parseDouble(words[3]); 35 | verts.add(new Vec3(x, y, z)); 36 | break; 37 | case "f": 38 | tris.add(Integer.parseInt(words[1].split("/")[0])-1); 39 | tris.add(Integer.parseInt(words[2].split("/")[0])-1); 40 | tris.add(Integer.parseInt(words[3].split("/")[0])-1); 41 | break; 42 | default: 43 | break; 44 | } 45 | } 46 | line = br.readLine(); 47 | } 48 | 49 | br.close(); 50 | 51 | int[] trisArr = new int[tris.size()]; 52 | for(int i = 0; i < tris.size(); i++) 53 | trisArr[i] = tris.get(i); 54 | 55 | Mesh mesh = new Mesh(verts.toArray(new Vec3[verts.size()]), trisArr); 56 | 57 | return mesh; 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/njine/assets/TextureLoader.java: -------------------------------------------------------------------------------- 1 | package njine.assets; 2 | 3 | import java.io.File; 4 | import java.io.FileNotFoundException; 5 | import java.io.IOException; 6 | 7 | import javax.imageio.ImageIO; 8 | 9 | public class TextureLoader implements AssetLoader 10 | { 11 | public Object load(String filename) throws FileNotFoundException, IOException 12 | { 13 | return ImageIO.read(new File(filename)); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/njine/camera/Camera.java: -------------------------------------------------------------------------------- 1 | package njine.camera; 2 | 3 | import njine.entity.Transform; 4 | import njine.math.Vec3; 5 | 6 | 7 | public abstract class Camera 8 | { 9 | private Transform transform; 10 | private double nearClip, farClip, fov; 11 | 12 | public Camera(double fov, double nearClip, double farClip) 13 | { 14 | this.transform = new Transform(Vec3.ZERO, Vec3.ZERO, Vec3.ZERO, Vec3.ZERO); 15 | this.fov = fov; 16 | this.nearClip = nearClip; 17 | this.farClip = farClip; 18 | } 19 | 20 | public Transform getTransform() 21 | { 22 | return transform; 23 | } 24 | 25 | public double getFOV() 26 | { 27 | return fov; 28 | } 29 | 30 | public double getNearClip() 31 | { 32 | return nearClip; 33 | } 34 | 35 | public double getFarClip() 36 | { 37 | return farClip; 38 | } 39 | 40 | public abstract void setup(); 41 | 42 | public abstract void update(); 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/njine/camera/FPCam.java: -------------------------------------------------------------------------------- 1 | package njine.camera; 2 | import java.awt.event.KeyEvent; 3 | 4 | import njine.math.Vec3; 5 | import njine.util.Input; 6 | 7 | 8 | public class FPCam extends Camera 9 | { 10 | public FPCam() 11 | { 12 | super(50, .1, 1000); 13 | } 14 | 15 | public void setup() 16 | { 17 | 18 | } 19 | 20 | public void update() 21 | { 22 | getTransform().setLocalRotation(new Vec3(Input.getMouseY()/2, (512-Input.getMouseX()/2), 0)); 23 | if(Input.getKeyPressed(KeyEvent.VK_W)) 24 | { 25 | Vec3 newPos = getTransform().getLocalMat().multVecMat(new Vec3(0,0,.1)).add(getTransform().getGlobalPosition()); 26 | getTransform().setGlobalPosition(newPos); 27 | } 28 | if(Input.getKeyPressed(KeyEvent.VK_S)) 29 | { 30 | Vec3 newPos = getTransform().getLocalMat().multVecMat(new Vec3(0,0,-.1)).add(getTransform().getGlobalPosition()); 31 | getTransform().setGlobalPosition(newPos); 32 | } 33 | if(Input.getKeyPressed(KeyEvent.VK_A)) 34 | { 35 | Vec3 newPos = getTransform().getLocalMat().multVecMat(new Vec3(.1,0,0)).add(getTransform().getGlobalPosition()); 36 | getTransform().setGlobalPosition(newPos); 37 | } 38 | if(Input.getKeyPressed(KeyEvent.VK_D)) 39 | { 40 | Vec3 newPos = getTransform().getLocalMat().multVecMat(new Vec3(-.1,0,0)).add(getTransform().getGlobalPosition()); 41 | getTransform().setGlobalPosition(newPos); 42 | } 43 | if(Input.getKeyPressed(KeyEvent.VK_Q)) 44 | { 45 | Vec3 newPos = getTransform().getLocalMat().multVecMat(new Vec3(0,.1,0)).add(getTransform().getGlobalPosition()); 46 | getTransform().setGlobalPosition(newPos); 47 | } 48 | if(Input.getKeyPressed(KeyEvent.VK_E)) 49 | { 50 | Vec3 newPos = getTransform().getLocalMat().multVecMat(new Vec3(0,-.1,0)).add(getTransform().getGlobalPosition()); 51 | getTransform().setGlobalPosition(newPos); 52 | } 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/njine/entity/Entity.java: -------------------------------------------------------------------------------- 1 | package njine.entity; 2 | 3 | import njine.math.Vec3; 4 | 5 | 6 | public abstract class Entity 7 | { 8 | private Transform transform; 9 | private Model model; 10 | 11 | public Entity() 12 | { 13 | this.transform = new Transform(Vec3.ZERO, Vec3.ZERO, Vec3.ZERO, Vec3.ZERO); 14 | this.model = new Model(new Mesh(new Vec3[]{}, new int[]{}), new Material()); 15 | } 16 | 17 | public abstract void setup(); 18 | 19 | public abstract void update(); 20 | 21 | public Transform getTransform() 22 | { 23 | return transform; 24 | } 25 | 26 | public Model getModel() 27 | { 28 | return model; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/njine/entity/Material.java: -------------------------------------------------------------------------------- 1 | package njine.entity; 2 | 3 | import java.awt.image.BufferedImage; 4 | 5 | public class Material 6 | { 7 | private BufferedImage texture; 8 | 9 | public Material(BufferedImage tex) 10 | { 11 | texture = tex; 12 | } 13 | 14 | public Material() 15 | { 16 | texture = new BufferedImage(0, 0, BufferedImage.TYPE_INT_RGB); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/njine/entity/Mesh.java: -------------------------------------------------------------------------------- 1 | package njine.entity; 2 | import java.util.ArrayList; 3 | 4 | import njine.math.Vec3; 5 | 6 | 7 | public class Mesh 8 | { 9 | private Vec3[] verts; 10 | private int[] tris; 11 | 12 | public Mesh(Vec3[] verts, int[] tris) 13 | { 14 | this.verts = verts; 15 | this.tris = tris; 16 | } 17 | 18 | public Vec3[] getVerts() 19 | { 20 | return verts; 21 | } 22 | 23 | public int[] getTris() 24 | { 25 | return tris; 26 | } 27 | 28 | public void setVerts(Vec3[] verts) 29 | { 30 | this.verts = verts; 31 | } 32 | 33 | public void setTris(int[] tris) 34 | { 35 | this.tris = tris; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/njine/entity/Model.java: -------------------------------------------------------------------------------- 1 | package njine.entity; 2 | 3 | 4 | public class Model 5 | { 6 | private Mesh mesh; 7 | private Material material; 8 | 9 | public Model(Mesh mesh, Material material) 10 | { 11 | this.mesh = mesh; 12 | this.material = material; 13 | } 14 | 15 | public Mesh getMesh() 16 | { 17 | return mesh; 18 | } 19 | 20 | public Material getMaterial() 21 | { 22 | return material; 23 | } 24 | 25 | public void setMesh(Mesh mesh) 26 | { 27 | this.mesh = mesh; 28 | } 29 | 30 | public void setMaterial(Material material) 31 | { 32 | this.material = material; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/njine/entity/Transform.java: -------------------------------------------------------------------------------- 1 | package njine.entity; 2 | 3 | import njine.math.Mat44; 4 | import njine.math.MatBuilder; 5 | import njine.math.Vec3; 6 | 7 | 8 | public class Transform 9 | { 10 | private Vec3 globalPosition; 11 | private Vec3 globalRotation; 12 | private Vec3 localPosition; 13 | private Vec3 localRotation; 14 | 15 | private Mat44 local; 16 | private Mat44 global; 17 | 18 | public Transform(Vec3 globalPosition, Vec3 globalRotation, Vec3 localPosition, Vec3 localRotation) 19 | { 20 | this.globalPosition = globalPosition; 21 | this.globalRotation = globalRotation; 22 | this.localPosition = localPosition; 23 | this.localRotation = localRotation; 24 | global = MatBuilder.translationMatrix(globalPosition.x, globalPosition.y, globalPosition.z); 25 | global = global.multiply(MatBuilder.rotationMatrix(globalRotation.x, globalRotation.y, globalRotation.z)); 26 | local = MatBuilder.translationMatrix(localPosition.x, localPosition.y, localPosition.z); 27 | local = local.multiply(MatBuilder.rotationMatrix(localRotation.x, localRotation.y, localRotation.z)); 28 | } 29 | 30 | public Vec3 getGlobalPosition() 31 | { 32 | return globalPosition; 33 | } 34 | 35 | public Vec3 getGlobalRotation() 36 | { 37 | return globalRotation; 38 | } 39 | 40 | public Vec3 getLocalPosition() 41 | { 42 | return localPosition; 43 | } 44 | 45 | public Vec3 getLocalRotation() 46 | { 47 | return localRotation; 48 | } 49 | 50 | public Mat44 getGlobalMat() 51 | { 52 | return global; 53 | } 54 | 55 | public Mat44 getLocalMat() 56 | { 57 | return local; 58 | } 59 | 60 | public void setGlobalPosition(Vec3 globalPosition) 61 | { 62 | this.globalPosition = globalPosition; 63 | global = MatBuilder.translationMatrix(globalPosition.x, globalPosition.y, globalPosition.z); 64 | global = global.multiply(MatBuilder.rotationMatrix(globalRotation.x, globalRotation.y, globalRotation.z)); 65 | } 66 | 67 | public void setGlobalRotation(Vec3 globalRotation) 68 | { 69 | this.globalRotation = globalRotation; 70 | global = MatBuilder.translationMatrix(globalPosition.x, globalPosition.y, globalPosition.z); 71 | global = global.multiply(MatBuilder.rotationMatrix(globalRotation.x, globalRotation.y, globalRotation.z)); 72 | } 73 | 74 | public void setLocalPosition(Vec3 localPosition) 75 | { 76 | this.localPosition = localPosition; 77 | local = MatBuilder.translationMatrix(localPosition.x, localPosition.y, localPosition.z); 78 | local = local.multiply(MatBuilder.rotationMatrix(localRotation.x, localRotation.y, localRotation.z)); 79 | } 80 | 81 | public void setLocalRotation(Vec3 localRotation) 82 | { 83 | this.localRotation = localRotation; 84 | local = MatBuilder.translationMatrix(localPosition.x, localPosition.y, localPosition.z); 85 | local = local.multiply(MatBuilder.rotationMatrix(localRotation.x, localRotation.y, localRotation.z)); 86 | } 87 | 88 | public String toString() 89 | { 90 | return "globalPosition: "+ globalPosition + ", globalRotation: " + globalRotation + ", localPosition: " + localPosition + ", localRotation: " + localRotation; 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /src/njine/math/Mat44.java: -------------------------------------------------------------------------------- 1 | package njine.math; 2 | 3 | 4 | public class Mat44 5 | { 6 | private double[][] m; 7 | 8 | public Mat44(double m00, double m01, double m02, double m03, 9 | double m10, double m11, double m12, double m13, 10 | double m20, double m21, double m22, double m23, 11 | double m30, double m31, double m32, double m33) 12 | { 13 | m = new double[4][4]; 14 | m[0][0]=m00;m[0][1]=m01;m[0][2]=m02;m[0][3]=m03; 15 | m[1][0]=m10;m[1][1]=m11;m[1][2]=m12;m[1][3]=m13; 16 | m[2][0]=m20;m[2][1]=m21;m[2][2]=m22;m[2][3]=m23; 17 | m[3][0]=m30;m[3][1]=m31;m[3][2]=m32;m[3][3]=m33; 18 | } 19 | 20 | public Mat44(double[][] m) 21 | { 22 | this.m = m; 23 | } 24 | 25 | public Mat44() 26 | { 27 | m = new double[][] {{1,0,0,0}, 28 | {0,1,0,0}, 29 | {0,0,1,0}, 30 | {0,0,0,1}}; 31 | } 32 | 33 | public void setMat(double[][] m) 34 | { 35 | this.m = m; 36 | } 37 | 38 | public void setMat(double m00, double m01, double m02, double m03, 39 | double m10, double m11, double m12, double m13, 40 | double m20, double m21, double m22, double m23, 41 | double m30, double m31, double m32, double m33) 42 | { 43 | m = new double[4][4]; 44 | m[0][0]=m00;m[0][1]=m01;m[0][2]=m02;m[0][3]=m03; 45 | m[1][0]=m10;m[1][1]=m11;m[1][2]=m12;m[1][3]=m13; 46 | m[2][0]=m20;m[2][1]=m21;m[2][2]=m22;m[2][3]=m23; 47 | m[3][0]=m30;m[3][1]=m31;m[3][2]=m32;m[3][3]=m33; 48 | } 49 | 50 | public double[][] getMat() 51 | { 52 | return m; 53 | } 54 | 55 | public Vec3 multVecMat(Vec3 v) 56 | { 57 | double w = 1; 58 | Vec3 ans = new Vec3(); 59 | ans.x = v.x*m[0][0] + v.y*m[1][0] + v.z*m[2][0] + w*m[3][0]; 60 | ans.y = v.x*m[0][1] + v.y*m[1][1] + v.z*m[2][1] + w*m[3][1]; 61 | ans.z = v.x*m[0][2] + v.y*m[1][2] + v.z*m[2][2] + w*m[3][2]; 62 | w = v.x*m[0][3] + v.y*m[1][3] + v.z*m[2][3] + w*m[3][3]; 63 | 64 | // for projection matricies 65 | if(w==1||w==0) 66 | return ans; 67 | ans.x/=w; 68 | ans.y/=w; 69 | ans.z/=w; 70 | return ans; 71 | } 72 | 73 | public Vec3 multVecMat(double[] v) 74 | { 75 | return multVecMat(new Vec3(v)); 76 | } 77 | 78 | public Mat44 multiply(Mat44 in) 79 | { 80 | /* 81 | double[][] m2 = in.getMat(); 82 | // assert(width1 == height2) 83 | if (m[0].length != m2.length) 84 | throw new ArithmeticException("Invalid matrix height"); 85 | 86 | int sum = 0; 87 | double[][] ans = new double[4][4]; 88 | for(int i=0; i < m.length; i++) 89 | { 90 | for(int j = 0; j < m[0].length; j++) 91 | { 92 | for(int k=0; k < m2.length; k++) 93 | { 94 | sum += m[i][k]*m2[k][j]; 95 | } 96 | ans[i][j] = sum; 97 | sum = 0; 98 | } 99 | } 100 | */ 101 | double[][] ans = multiplicar(m, in.getMat()); 102 | 103 | return new Mat44(ans); 104 | } 105 | 106 | public static double[][] multiplicar(double[][] A, double[][] B) { 107 | 108 | int aRows = A.length; 109 | int aColumns = A[0].length; 110 | int bRows = B.length; 111 | int bColumns = B[0].length; 112 | 113 | if (aColumns != bRows) { 114 | throw new IllegalArgumentException("A:Rows: " + aColumns + " did not match B:Columns " + bRows + "."); 115 | } 116 | 117 | double[][] C = new double[aRows][bColumns]; 118 | for (int i = 0; i < 2; i++) { 119 | for (int j = 0; j < 2; j++) { 120 | C[i][j] = 0.00000; 121 | } 122 | } 123 | 124 | for (int i = 0; i < aRows; i++) { // aRow 125 | for (int j = 0; j < bColumns; j++) { // bColumn 126 | for (int k = 0; k < aColumns; k++) { // aColumn 127 | C[i][j] += A[i][k] * B[k][j]; 128 | } 129 | } 130 | } 131 | 132 | return C; 133 | } 134 | 135 | 136 | public Mat44 invert() 137 | { 138 | double[][] a = (new Mat44(m)).getMat(); 139 | int n = a.length; 140 | double x[][] = new double[n][n]; 141 | double b[][] = new double[n][n]; 142 | int index[] = new int[n]; 143 | for (int i=0; i=0; --j) 161 | { 162 | x[j][i] = b[index[j]][i]; 163 | for (int k=j+1; k c1) c1 = c0; 190 | } 191 | c[i] = c1; 192 | } 193 | 194 | // Search the pivoting element from each column 195 | int k = 0; 196 | for (int j=0; j pi1) 204 | { 205 | pi1 = pi0; 206 | k = i; 207 | } 208 | } 209 | 210 | // Interchange rows according to the pivoting order 211 | int itmp = index[j]; 212 | index[j] = index[k]; 213 | index[k] = itmp; 214 | for (int i=j+1; i winWidth-1) && !(maxx < 0) && !(miny > winHeight-1) && !(maxy < 0)) 79 | { 80 | int x0 = Math.max(0, minx); 81 | int x1 = Math.min(winWidth-1, maxx); 82 | int y0 = Math.max(0, miny); 83 | int y1 = Math.min(winHeight-1, maxy); 84 | 85 | double area = edgeFunction(p1, p2, p3); 86 | 87 | for(int y = y0; y <= y1; y++) 88 | { 89 | for(int x = x0; x <= x1; x++) 90 | { 91 | //if (x > winWidth-1 || x < 0 || y > winHeight-1 || y < 0) break; 92 | 93 | double w0 = edgeFunction(p2, p3, new Vec3(x, y, 0)); 94 | double w1 = edgeFunction(p3, p1, new Vec3(x, y, 0)); 95 | double w2 = edgeFunction(p1, p2, new Vec3(x, y, 0)); 96 | 97 | if((w0 >= 0 && w1 >= 0 && w2 >= 0) || (w0 <= 0 && w1 <= 0 && w2 <= 0)) 98 | { 99 | w0 /= area; 100 | w1 /= area; 101 | w2 /= area; 102 | 103 | double oneOverZ = p1.z * w0 + p2.z * w1 + p3.z * w2; 104 | double z = 1 / oneOverZ; 105 | //int rgb = depthbuffer.getRGB(x, y); 106 | if(z < depthbuffer[y*winWidth+x]) 107 | { 108 | depthbuffer[y*winWidth+x] = z; 109 | fbGraphics.drawLine(x, y, x, y); 110 | } 111 | } 112 | } 113 | } 114 | } 115 | } 116 | 117 | BufferedImage depthbufferImg = new BufferedImage(winWidth, winHeight, BufferedImage.TYPE_INT_RGB); 118 | for(int y = 0; y < winWidth; y++) 119 | { 120 | for(int x = 0; x < winHeight; x++) 121 | { 122 | int grayLevel = (int)(depthbuffer[y*winWidth+x]*100); 123 | depthbufferImg.setRGB(x, y, (grayLevel << 16) + (grayLevel << 8) + grayLevel); 124 | } 125 | } 126 | 127 | return depthbufferImg; 128 | } 129 | 130 | double edgeFunction(Vec3 a, Vec3 b, Vec3 c) 131 | { 132 | return (c.x - a.x) * (b.y - a.y) - (c.y - a.y) * (b.x - a.x); 133 | } 134 | 135 | Vec3 cvtNDCToRaster(Vec3 ndc) 136 | { 137 | Vec3 raster = new Vec3(); 138 | raster.x = (ndc.x+1) * .5 * winWidth; 139 | raster.y = (1-ndc.y) * .5 * winHeight; 140 | raster.z = ndc.z; 141 | return raster; 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/njine/render/RendererThread.java: -------------------------------------------------------------------------------- 1 | package njine.render; 2 | import java.awt.image.BufferedImage; 3 | 4 | import javax.swing.JFrame; 5 | 6 | 7 | public class RendererThread extends Thread 8 | { 9 | private Renderer renderer; 10 | private RenderSurface surface; 11 | private JFrame window; 12 | 13 | 14 | public RendererThread(Renderer renderer, RenderSurface surface, JFrame window) 15 | { 16 | this.renderer = renderer; 17 | this.surface = surface; 18 | this.window = window; 19 | } 20 | 21 | public void run() 22 | { 23 | while(window.isActive()) 24 | { 25 | BufferedImage framebuffer = renderer.render(); 26 | surface.setFramebuffer(framebuffer); 27 | window.repaint(); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/njine/render/VertexAttribs.java: -------------------------------------------------------------------------------- 1 | package njine.render; 2 | import java.util.ArrayList; 3 | 4 | 5 | public class VertexAttribs 6 | { 7 | private ArrayList attribs; 8 | 9 | public VertexAttribs(ArrayList attribs) 10 | { 11 | this.attribs = attribs; 12 | } 13 | 14 | public ArrayList getAttrib() 15 | { 16 | return attribs; 17 | } 18 | 19 | public void setAttrib(ArrayList attribs) 20 | { 21 | this.attribs = attribs; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/njine/util/Input.java: -------------------------------------------------------------------------------- 1 | package njine.util; 2 | 3 | import java.awt.event.KeyEvent; 4 | import java.awt.event.KeyListener; 5 | import java.awt.event.MouseEvent; 6 | import java.awt.event.MouseMotionListener; 7 | import java.util.HashMap; 8 | 9 | public class Input implements MouseMotionListener, KeyListener 10 | { 11 | private enum KeyStatus 12 | { 13 | PRESSED, NOT_PRESSED; 14 | } 15 | 16 | private int mouseX; 17 | private int mouseY; 18 | private static Input instance = new Input(); 19 | 20 | private HashMap keyMap; 21 | 22 | private Input() 23 | { 24 | keyMap = new HashMap(); 25 | } 26 | 27 | public void keyPressed(KeyEvent e) 28 | { 29 | keyMap.put(e.getKeyCode(), KeyStatus.PRESSED); 30 | } 31 | 32 | public void keyReleased(KeyEvent e) 33 | { 34 | keyMap.put(e.getKeyCode(), KeyStatus.NOT_PRESSED); 35 | } 36 | 37 | public void keyTyped(KeyEvent e) 38 | { 39 | 40 | } 41 | 42 | public void mouseDragged(MouseEvent e) 43 | { 44 | mouseX = e.getX(); 45 | mouseY = e.getY(); 46 | } 47 | 48 | public void mouseMoved(MouseEvent e) 49 | { 50 | mouseX = e.getX(); 51 | mouseY = e.getY(); 52 | } 53 | 54 | public static boolean getKeyPressed(int keyCode) 55 | { 56 | return instance.keyMap.get(keyCode) == KeyStatus.PRESSED; 57 | } 58 | 59 | public static int getMouseX() 60 | { 61 | return instance.mouseX; 62 | } 63 | 64 | public static int getMouseY() 65 | { 66 | return instance.mouseY; 67 | } 68 | 69 | public static Input getInstance() 70 | { 71 | return instance; 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/njine/world/AppConfig.java: -------------------------------------------------------------------------------- 1 | package njine.world; 2 | 3 | public class AppConfig 4 | { 5 | public int winWidth; 6 | public int winHeight; 7 | public String winName; 8 | 9 | public AppConfig() 10 | { 11 | winWidth = 512; 12 | winHeight = 512; 13 | winName = "Test"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/njine/world/World.java: -------------------------------------------------------------------------------- 1 | package njine.world; 2 | import java.util.ArrayList; 3 | import java.util.Timer; 4 | import java.util.concurrent.Executors; 5 | import java.util.concurrent.ScheduledExecutorService; 6 | import java.util.concurrent.TimeUnit; 7 | 8 | import javax.swing.JFrame; 9 | 10 | import njine.assets.AssetManager; 11 | import njine.assets.OBJLoader; 12 | import njine.camera.Camera; 13 | import njine.entity.Entity; 14 | import njine.entity.Mesh; 15 | import njine.math.Mat44; 16 | import njine.math.Vec3; 17 | import njine.render.RenderSurface; 18 | import njine.render.Renderer; 19 | import njine.render.RendererThread; 20 | import njine.util.Input; 21 | 22 | 23 | public class World 24 | { 25 | private ArrayList entities; 26 | private Camera camera; 27 | private AppConfig config; 28 | private Renderer renderer; 29 | private Mesh worldMesh; 30 | 31 | public World(AppConfig appConfig) 32 | { 33 | entities = new ArrayList(); 34 | camera = null; 35 | worldMesh = null; 36 | config = appConfig; 37 | renderer = new Renderer(); 38 | } 39 | 40 | public void begin() 41 | { 42 | if(camera == null) 43 | throw new IllegalStateException("must begin world with at least one camera"); 44 | if(entities.size() == 0) 45 | throw new IllegalStateException("must begin world with at least one entity"); 46 | 47 | // window setup 48 | JFrame window = new JFrame(config.winName); 49 | window.setSize(config.winWidth, config.winHeight); 50 | window.setVisible(true); 51 | window.setResizable(false); 52 | window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 53 | window.addMouseMotionListener(Input.getInstance()); 54 | window.addKeyListener(Input.getInstance()); 55 | RenderSurface surface = new RenderSurface(); 56 | window.add(surface); 57 | 58 | setupEntities(); 59 | camera.setup(); 60 | 61 | RendererThread rendererThread = new RendererThread(renderer, surface, window); 62 | rendererThread.start(); 63 | 64 | updateEntities(); 65 | } 66 | 67 | public void setCamera(Camera camera) 68 | { 69 | this.camera = camera; 70 | Mat44 wToC = camera.getTransform().getGlobalMat().invert().multiply(camera.getTransform().getLocalMat().invert()); 71 | renderer.init(config.winHeight, config.winWidth, wToC, camera.getFOV(), camera.getNearClip(), camera.getFarClip()); 72 | } 73 | 74 | public void addEntity(Entity e) 75 | { 76 | entities.add(e); 77 | } 78 | 79 | 80 | private Mesh entitiesToMesh() 81 | { 82 | ArrayList verts = new ArrayList(); 83 | ArrayList tris = new ArrayList(); 84 | int vertSize = 0; 85 | 86 | for(Entity e: entities) 87 | { 88 | for(Vec3 v: e.getModel().getMesh().getVerts()) 89 | { 90 | Vec3 local = e.getTransform().getLocalMat().multVecMat(v); 91 | Vec3 world = e.getTransform().getGlobalMat().multVecMat(local); 92 | verts.add(world); 93 | } 94 | 95 | for(Integer i: e.getModel().getMesh().getTris()) 96 | { 97 | tris.add(vertSize + i); 98 | } 99 | 100 | vertSize = verts.size(); 101 | } 102 | 103 | Vec3[] vertsArr = new Vec3[verts.size()]; 104 | int[] trisArr = new int[tris.size()]; 105 | for(int i = 0; i < verts.size(); i++) 106 | vertsArr[i] = verts.get(i); 107 | for(int j = 0; j < tris.size(); j++) 108 | trisArr[j] = tris.get(j); 109 | 110 | 111 | return new Mesh(vertsArr, trisArr); 112 | } 113 | 114 | private void setupEntities() 115 | { 116 | worldMesh = entitiesToMesh(); 117 | renderer.setMesh(worldMesh); 118 | 119 | for(Entity e: entities) 120 | e.setup(); 121 | } 122 | 123 | private void updateEntities() 124 | { 125 | Runnable updateRunnable = 126 | new Runnable() 127 | { 128 | public void run() 129 | { 130 | camera.update(); 131 | Mat44 wToC = camera.getTransform().getGlobalMat().invert().multiply(camera.getTransform().getLocalMat().invert()); 132 | renderer.setWorldToCam(wToC); 133 | for(Entity e: entities) 134 | e.update(); 135 | 136 | worldMesh = entitiesToMesh(); 137 | renderer.setMesh(worldMesh); 138 | } 139 | }; 140 | ScheduledExecutorService executor = Executors.newScheduledThreadPool(1); 141 | executor.scheduleAtFixedRate(updateRunnable, 0, 30, TimeUnit.MILLISECONDS); 142 | } 143 | } 144 | --------------------------------------------------------------------------------