Emptying repo for new version of site
13
Makefile
|
|
@ -1,13 +0,0 @@
|
||||||
entrypoint=index.html
|
|
||||||
|
|
||||||
postbuild: build
|
|
||||||
node postbuild.js
|
|
||||||
|
|
||||||
build: node_modules
|
|
||||||
npx parcel build ${entrypoint}
|
|
||||||
|
|
||||||
node_modules:
|
|
||||||
npm install
|
|
||||||
|
|
||||||
clean:
|
|
||||||
git clean -xdf
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
<html>
|
|
||||||
<body>
|
|
||||||
<script type='module' src="./src/index.jsx"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
8066
package-lock.json
generated
42
package.json
|
|
@ -1,42 +0,0 @@
|
||||||
{
|
|
||||||
"name": "portfolio-site",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "Portfolio Site",
|
|
||||||
"scripts": {
|
|
||||||
"start": "rm -v dist/* && node postbuild.js && parcel index.html",
|
|
||||||
"build": "rm -rv dist && parcel build index.html && exec node postbuild.js"
|
|
||||||
},
|
|
||||||
"browserslist": [
|
|
||||||
"defaults",
|
|
||||||
"not IE 11",
|
|
||||||
"maintained node versions"
|
|
||||||
],
|
|
||||||
"author": "",
|
|
||||||
"license": "BSD-2-Clause",
|
|
||||||
"devDependencies": {
|
|
||||||
"@babel/core": "^7.12.3",
|
|
||||||
"@babel/plugin-transform-react-jsx": "^7.12.5",
|
|
||||||
"cssnano": "^5.1.0",
|
|
||||||
"parcel": "^2.7.0",
|
|
||||||
"typescript": "^4.0.5"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@material-ui/core": "^4.11.0",
|
|
||||||
"@material-ui/icons": "^4.11.2",
|
|
||||||
"@types/react": "^16.9.56",
|
|
||||||
"fontsource-roboto": "^3.0.3",
|
|
||||||
"hls.js": "^1.1.5",
|
|
||||||
"preact": "^10.5.5",
|
|
||||||
"preact-async-route": "^2.2.1",
|
|
||||||
"preact-router": "^3.2.1",
|
|
||||||
"react": "^16.14.0",
|
|
||||||
"react-dom": "^16.14.0",
|
|
||||||
"video.js": "^7.10.2"
|
|
||||||
},
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"alias": {
|
|
||||||
"react": "preact/compat",
|
|
||||||
"react-dom/test-utils": "preact/test-utils",
|
|
||||||
"react-dom": "preact/compat"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
57
postbuild.js
|
|
@ -1,57 +0,0 @@
|
||||||
const Path = require("path");
|
|
||||||
const fs = require("fs");
|
|
||||||
|
|
||||||
fs.copyFile("./static/video/background_video/index0.ts",
|
|
||||||
"./dist/index0.ts", (err) => {
|
|
||||||
if (err) {
|
|
||||||
return
|
|
||||||
console.error("Video segments were not copied! \n",
|
|
||||||
err);
|
|
||||||
} else {
|
|
||||||
return console.error("Video segments successfully copied! \n");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
fs.copyFile("./static/video/background_video/index1.ts", "./dist/index1.ts", (err) => {
|
|
||||||
if (err) {
|
|
||||||
return console.error("Video segments were not copied! \n", err);
|
|
||||||
} else {
|
|
||||||
return console.error("Video segments successfully copied! \n");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
fs.copyFile("./static/video/background_video/index2.ts", "./dist/index2.ts", (err) => {
|
|
||||||
if (err) {
|
|
||||||
return console.error("Video segments were not copied! \n", err);
|
|
||||||
} else {
|
|
||||||
return console.error("Video segments successfully copied! \n");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
fs.copyFile("./static/video/background_video/index3.ts", "./dist/index3.ts", (err) => {
|
|
||||||
if (err) {
|
|
||||||
return console.error("Video segments were not copied! \n", err);
|
|
||||||
} else {
|
|
||||||
return console.error("Video segments successfully copied! \n");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
fs.copyFile("./static/video/background_video/index4.ts", "./dist/index4.ts", (err) => {
|
|
||||||
if (err) {
|
|
||||||
return console.error("Video segments were not copied! \n", err);
|
|
||||||
} else {
|
|
||||||
return console.error("Video segments successfully copied! \n");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
fs.copyFile("./static/video/background_video/index5.ts", "./dist/index5.ts", (err) => {
|
|
||||||
if (err) {
|
|
||||||
return console.error("Video segments were not copied! \n", err);
|
|
||||||
} else {
|
|
||||||
return console.error("Video segments successfully copied! \n");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
fs.copyFile("./static/video/background_video/index6.ts", "./dist/index6.ts", (err) => {
|
|
||||||
if (err) {
|
|
||||||
return console.error("Video segments were not copied! \n", err);
|
|
||||||
} else {
|
|
||||||
return console.error("Video segments successfully copied! \n");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
import { h, render, Component } from "preact";
|
|
||||||
import {
|
|
||||||
Container,
|
|
||||||
Card,
|
|
||||||
CardContent,
|
|
||||||
Typography,
|
|
||||||
} from "@material-ui/core";
|
|
||||||
|
|
||||||
class LostPage extends Component {
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<span>
|
|
||||||
<Container maxWidth="sm" >
|
|
||||||
<Card>
|
|
||||||
<CardContent>
|
|
||||||
<Typography variant="h1"> Error 404</Typography>
|
|
||||||
<Typography variant="body1">
|
|
||||||
There is no content here...
|
|
||||||
</Typography>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</Container>
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default LostPage;
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
import { h, render, Component } from "preact";
|
|
||||||
const background = new URL("../static/javascript/background.js", import.meta.url);
|
|
||||||
|
|
||||||
class Background extends Component {
|
|
||||||
componentDidMount() {
|
|
||||||
const script = document.createElement("script");
|
|
||||||
script.src = background;
|
|
||||||
script.async = true;
|
|
||||||
document.body.appendChild(script);
|
|
||||||
}
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<span>
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Background;
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
import { h, render, Component } from "preact";
|
|
||||||
import Header from "./Header";
|
|
||||||
import {
|
|
||||||
Container,
|
|
||||||
Card,
|
|
||||||
CardHeader,
|
|
||||||
CardContent,
|
|
||||||
Typography,
|
|
||||||
List,
|
|
||||||
ListItem,
|
|
||||||
ListItemText,
|
|
||||||
Link,
|
|
||||||
} from "@material-ui/core";
|
|
||||||
|
|
||||||
class Contact extends Component {
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<span>
|
|
||||||
<Header />
|
|
||||||
<Container maxWidth="md">
|
|
||||||
<p />
|
|
||||||
|
|
||||||
<Card>
|
|
||||||
<CardHeader title="Contact" />
|
|
||||||
<CardContent>
|
|
||||||
<ListItemText>
|
|
||||||
LinkedIn:{" "}
|
|
||||||
<Link
|
|
||||||
rel="noopener"
|
|
||||||
href="https://www.linkedin.com/in/warwick-new-96491a147/ "
|
|
||||||
>
|
|
||||||
https://www.linkedin.com/in/warwick-new-96491a147/
|
|
||||||
</Link>
|
|
||||||
</ListItemText>
|
|
||||||
<ListItemText>
|
|
||||||
Email:{" "}
|
|
||||||
<Link rel="noopener" href="mailto:warwick.l.e.new@gmail.com">
|
|
||||||
warwick.l.e.new@gmail.com
|
|
||||||
</Link>
|
|
||||||
</ListItemText>
|
|
||||||
<ListItemText>
|
|
||||||
Phone Number:{" "}
|
|
||||||
<Link rel="noopener" href="tel:07445728181">
|
|
||||||
07445 728 181
|
|
||||||
</Link>
|
|
||||||
</ListItemText>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</Container>
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Contact;
|
|
||||||
|
|
@ -1,132 +0,0 @@
|
||||||
import { h, render, Component } from "preact";
|
|
||||||
import {
|
|
||||||
Grid,
|
|
||||||
Container,
|
|
||||||
Card,
|
|
||||||
CardHeader,
|
|
||||||
CardMedia,
|
|
||||||
CardActions,
|
|
||||||
CardContent,
|
|
||||||
Typography,
|
|
||||||
Button,
|
|
||||||
} from "@material-ui/core";
|
|
||||||
import Monq from "../static/images/Monq.jpg";
|
|
||||||
import Graphics from "../static/images/Graphics.png";
|
|
||||||
import NewGraphics from "../static/images/NewGraphics.png";
|
|
||||||
import Code from "../static/images/code.png";
|
|
||||||
|
|
||||||
class GameProjects extends Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.content = [
|
|
||||||
{
|
|
||||||
header: "Current Graphics Project",
|
|
||||||
subheader: `Personal Side Project - Programmer: C++, Opengl, SDL`,
|
|
||||||
image: NewGraphics,
|
|
||||||
imageAltText: "Graphics",
|
|
||||||
content: `I'm currently working on another graphics project as this is
|
|
||||||
the area of computing I want to get back to working in. I plan on
|
|
||||||
generating a cyberpunk city and implementing other features that you
|
|
||||||
may find in a game engine, such as including a physics engine for a
|
|
||||||
more complex character controller and an entity system so I can
|
|
||||||
implement vehicles and other methods of terrain traversal.`,
|
|
||||||
buttonText: "Read Blog",
|
|
||||||
buttonLink: "/graphics-blog",
|
|
||||||
newtab: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
header: "Current Low Level Networking Project",
|
|
||||||
subheader: `Personal Side Project - Programmer: C, Sockets and Protobuf`,
|
|
||||||
image: Code,
|
|
||||||
imageAltText: "Networking Code",
|
|
||||||
content: `I'm currently working on another low level project as
|
|
||||||
interested in learning plain C and low level networking. I plan on
|
|
||||||
creating a mud that can potentially be used in conjunction with my
|
|
||||||
graphics project to create a multiplayer experience.`,
|
|
||||||
buttonText: "Visit Repo",
|
|
||||||
buttonLink: "https://github.com/WarwickNew/urchin",
|
|
||||||
newtab: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
header: "Monq",
|
|
||||||
subheader: `D-tail Entertainment - Programmer: Unreal Engine 4, C++,
|
|
||||||
Blueprints`,
|
|
||||||
image: Monq,
|
|
||||||
imageAltText: "Monq",
|
|
||||||
content: `Monq was developed by an indie startup which I founded
|
|
||||||
alongside classmates from my university. It was developed in Unreal
|
|
||||||
Engine 4 using a mixture of C++ and blueprints. I predominantly worked
|
|
||||||
on the AI mechanics and puzzle implementation of the game, though due
|
|
||||||
to the nature of working in a small team I was able to contribute to
|
|
||||||
puzzle design and texture generation through shaders.`,
|
|
||||||
buttonText: "Learn More",
|
|
||||||
buttonLink: "/monq",
|
|
||||||
newtab: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
header: "University Graphics Project",
|
|
||||||
subheader: `Falmouth University (Bsc hons: Computing for Games) -
|
|
||||||
Programmer: C++,
|
|
||||||
Opengl, SDL`,
|
|
||||||
image: Graphics,
|
|
||||||
imageAltText: "Generated terrain",
|
|
||||||
content: `This is where I learned how to create a graphics engine from
|
|
||||||
scratch using OpenGL, and was the project which I enjoyed the most
|
|
||||||
during my bachelor's degree. Here I learned the basics of GLSL shaders
|
|
||||||
to achive flat face lighting, blending colours based on position and
|
|
||||||
generating meshes based on perlin noise. I even created a movement
|
|
||||||
system which keeps the camera a fixed distance above the noise value.
|
|
||||||
I especially enjoyed how deep I jumped into C++ to improve its memory
|
|
||||||
footprint returning to this project in my final year of university,
|
|
||||||
leaning more about pointers and memory management.`,
|
|
||||||
buttonText: "Learn More",
|
|
||||||
buttonLink: "/uni-graphics",
|
|
||||||
newtab: false
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
render = () => {
|
|
||||||
return (
|
|
||||||
<Container>
|
|
||||||
<p />
|
|
||||||
<Card>
|
|
||||||
<CardHeader title="Game Development Projects" />
|
|
||||||
</Card>
|
|
||||||
<p />
|
|
||||||
<Grid container spacing={3}>
|
|
||||||
{this.content.map((project) => {
|
|
||||||
return (
|
|
||||||
<Grid item xs="12" lg="6">
|
|
||||||
<Card>
|
|
||||||
<CardHeader
|
|
||||||
title={project.header}
|
|
||||||
subheader={project.subheader}
|
|
||||||
/>
|
|
||||||
<CardMedia
|
|
||||||
style={{ height: 0, paddingTop: "25%" }}
|
|
||||||
image={project.image}
|
|
||||||
title={project.imageAltText}
|
|
||||||
/>
|
|
||||||
<CardContent>
|
|
||||||
<Typography variant="body1">{project.content}</Typography>
|
|
||||||
</CardContent>
|
|
||||||
<CardActions>
|
|
||||||
<Button
|
|
||||||
target={project.newtab ? "_blank" : ""}
|
|
||||||
href={project.buttonLink}
|
|
||||||
color="primary"
|
|
||||||
>
|
|
||||||
{project.buttonText}
|
|
||||||
</Button>
|
|
||||||
</CardActions>
|
|
||||||
</Card>
|
|
||||||
</Grid>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Grid>
|
|
||||||
</Container>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
export default GameProjects;
|
|
||||||
|
|
@ -1,260 +0,0 @@
|
||||||
import { h, render, Component } from "preact";
|
|
||||||
import {
|
|
||||||
Grid,
|
|
||||||
Container,
|
|
||||||
Card,
|
|
||||||
CardHeader,
|
|
||||||
CardMedia,
|
|
||||||
CardActions,
|
|
||||||
CardContent,
|
|
||||||
Typography,
|
|
||||||
Button,
|
|
||||||
List,
|
|
||||||
ListItemIcon,
|
|
||||||
ListItem,
|
|
||||||
Link,
|
|
||||||
} from "@material-ui/core";
|
|
||||||
import ArrowRightIcon from '@material-ui/icons/ArrowRight';
|
|
||||||
import Header from "./Header";
|
|
||||||
|
|
||||||
import Blog1 from "../static/images/GraphicsBlog/blog1.png";
|
|
||||||
const Blog2 = new URL("../static/video/graphics_blog/blog2.webm", import.meta.url);
|
|
||||||
import Blog3 from "../static/images/GraphicsBlog/blog3.png";
|
|
||||||
import Blog4 from "../static/images/GraphicsBlog/blog4.png";
|
|
||||||
import Blog5 from "../static/images/GraphicsBlog/blog5.png";
|
|
||||||
import Blog6 from "../static/images/GraphicsBlog/blog6.png";
|
|
||||||
const Blog7 = new URL("../static/video/graphics_blog/blog7.mp4", import.meta.url);
|
|
||||||
const Blog8 = new URL("../static/video/graphics_blog/blog8.mp4", import.meta.url);
|
|
||||||
const Blog9 = new URL("../static/video/graphics_blog/blog9.mp4", import.meta.url);
|
|
||||||
const Blog10 = new URL("../static/video/graphics_blog/blog10.mp4", import.meta.url);
|
|
||||||
|
|
||||||
class GraphicsBlog extends Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.content = [
|
|
||||||
{
|
|
||||||
header: "SDL/OpenGL boilerplate",
|
|
||||||
subheader: "Note: this blog is a copy of a forum thread",
|
|
||||||
mediaType: "img",
|
|
||||||
image: Blog1,
|
|
||||||
imageAltText: "Textured square",
|
|
||||||
content: `Today I added textures to my game engine. Next up
|
|
||||||
refactoring... But then camera movement (Or strangely enough world
|
|
||||||
movement around the camera).`,
|
|
||||||
buttonText: undefined,
|
|
||||||
buttonLink: undefined,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
header: "Camera Movement",
|
|
||||||
subheader: "Note: this blog is a copy of a forum thread",
|
|
||||||
mediaType: "video",
|
|
||||||
image: Blog2,
|
|
||||||
imageAltText: "Textured square with camera movement",
|
|
||||||
content: `The project is now officially 3D, with camera movement. Next
|
|
||||||
up is more complex meshes.`,
|
|
||||||
buttonText: undefined,
|
|
||||||
buttonLink: undefined,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
header: "Multiple Meshes",
|
|
||||||
subheader: "Note: this blog is a copy of a forum thread",
|
|
||||||
mediaType: "img",
|
|
||||||
image: Blog3,
|
|
||||||
imageAltText: "2 textured squares",
|
|
||||||
content: `Now that I have a mesh class it's pretty easy to make multiple objects reusing textures and indices. Next, it's giving those meshes their own relative position rather than hard coding every position on every triangle point to move it 🙃.`,
|
|
||||||
buttonText: undefined,
|
|
||||||
buttonLink: undefined,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
header: "Models with their own Model space.",
|
|
||||||
subheader: "Note: this blog is a copy of a forum thread",
|
|
||||||
mediaType: "img",
|
|
||||||
image: Blog4,
|
|
||||||
imageAltText: "3 Textured squares",
|
|
||||||
content: <span>Models now have their own position in world space and
|
|
||||||
can be made up of multiple meshes. They can also be moved
|
|
||||||
dynamically.<p /> I got 3 different directions to go now. 1) start
|
|
||||||
working on a lighting system which will teach me more about low level
|
|
||||||
shaders and their maths, 2) create a method of loading in models
|
|
||||||
exported from blender, 3) start messing about with creating and
|
|
||||||
rendering shapes with noise. <p /> Comment) lighting is fun <p />
|
|
||||||
Answer) Light is cool, but to get good lighting I'm gonna need to
|
|
||||||
start working with vertex normals and UVs and that's without shadows.
|
|
||||||
So maybe it'll actually be worth me importing models where I know
|
|
||||||
this stuff isn't broken first, and then messing about with lighting
|
|
||||||
on that. idk, we'll see how I feel once I optimise what I
|
|
||||||
have.</span>,
|
|
||||||
buttonText: undefined,
|
|
||||||
buttonLink: undefined,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
header: "Loading models into the engine.",
|
|
||||||
subheader: "",
|
|
||||||
mediaType: "img",
|
|
||||||
image: Blog5,
|
|
||||||
imageAltText: "3 stages of model loading described in the description box",
|
|
||||||
content: "I decided I wanted something in my engine to test lighting on before I go further, I ran into a few problems along the way but here's the visual progression of things. The first image was me not sending the correct texture reference to the GPU followed by loading in the texture upside down, you know normal debugging stuff. It took me a few tries to get the textures loaded incorrectly as you can see but once I reverse-engineered some boilerplate example code for the order of execution when loading multiple textures the rest just fell into place.",
|
|
||||||
buttonText: undefined,
|
|
||||||
buttonLink: undefined,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
header: "Basic Lighting.",
|
|
||||||
subheader: "",
|
|
||||||
mediaType: "img",
|
|
||||||
image: Blog6,
|
|
||||||
imageAltText: "A graphical render of a coloured cube and a guitar backpack with a simple light.",
|
|
||||||
content: "Now I have a model loaded I've decided to focus on lighting for a bit. After implementing some simple diffuse lighting here I think I'm gonna go all in and jump straight to a PBR system.",
|
|
||||||
buttonText: undefined,
|
|
||||||
buttonLink: undefined,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
header: "Metalness",
|
|
||||||
subheader: "",
|
|
||||||
mediaType: "video",
|
|
||||||
image: Blog7,
|
|
||||||
imageAltText: "A render of light moving around a cube and a barrel",
|
|
||||||
content: <span>I've decided to go with the metalness, roughness, and AO version of PBR as the online documentation for its implementation appears far more accessible, special thanks to <Link component="span" rel="noopener" href="https://learnopengl.com/PBR/Theory" >LearnOpenGL.com</Link> In this video you can see a slightly wonky metalness implementation where the specular part of the algorithm appears to be inverted. I also expanded the material loading system to load in the Metalness.</span>,
|
|
||||||
buttonText: undefined,
|
|
||||||
buttonLink: undefined,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
header: "Metalness and Roughness",
|
|
||||||
subheader: "",
|
|
||||||
mediaType: "video",
|
|
||||||
image: Blog8,
|
|
||||||
imageAltText: "A render of light moving around a cube and a barrel",
|
|
||||||
content: <span>It was almost too easy but thanks to my metalness implementation I've been able to easily extend the material system to load multiple additional textures into the shader. making implementing roughness as easy as plugging it into the algorithm I've implemented for metalness.</span>,
|
|
||||||
buttonText: undefined,
|
|
||||||
buttonLink: undefined,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
header: "Full PBR",
|
|
||||||
subheader: "",
|
|
||||||
mediaType: "video",
|
|
||||||
image: Blog9,
|
|
||||||
imageAltText: "A render of light moving around a cube and a barrel",
|
|
||||||
content: <span>And here we have full PBR in the flesh. I think the only thing that's missing now is normal maps. I've also packed all the separate PBR textures into a single RMA texture, meaning that I only need to load one texture for the roughness, metalness and AO. Whilst I've got PBR working I think that for this project using a cube map with IBL to create realistic reflections will be overkill unless I really think environmental reflections are important for reflective surfaces like windows. But if I use a cube map for that situation, the effort will be better spent using one to mimic an interior instead.</span>,
|
|
||||||
buttonText: undefined,
|
|
||||||
buttonLink: undefined,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
header: "Normal Maps",
|
|
||||||
subheader: "",
|
|
||||||
mediaType: "video",
|
|
||||||
image: Blog10,
|
|
||||||
imageAltText: "A render of light moving around a cube and a barrel",
|
|
||||||
content: <span>It's been great to have the opportunity to dive into a hard maths topic again as I haven't felt this challenged since my <Link href='/uni-graphics'>university graphics project</Link>. I can now say I have an intuitive understanding of linear trigonometry again. Especially after reimplementing normal maps fully in the geometry shader using the triangle primitives available in that part of the pipeline to calculate the tangent space in the GPU rather than doing so on the CPU like on <Link component="span" rel="noopener" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping" >LearnOpenGL.com</Link>. If anyone needs an amazing refresher on linear algebra I can't recommend <Link component="span" rel="noopener" href="https://youtube.com/playlist?list=PLZHQObOWTQDPD3MizzM2xVFitgF8hE_ab">3Blue1Brown's series on the topic</Link> more. </span>,
|
|
||||||
buttonText: undefined,
|
|
||||||
buttonLink: undefined,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
render = () => {
|
|
||||||
return (
|
|
||||||
<span>
|
|
||||||
<Header />
|
|
||||||
<Container>
|
|
||||||
<p />
|
|
||||||
<Card>
|
|
||||||
<CardHeader title="Graphics Blog" />
|
|
||||||
<CardContent>
|
|
||||||
<Typography variant="p" >
|
|
||||||
In this project, I am going to generate a cyberpunk cityscape with
|
|
||||||
an infinite level of verticality to give it a large sense of
|
|
||||||
scale. The project is written in C++ using SDL to manage
|
|
||||||
windows and OpenGL to render the meshes.
|
|
||||||
</Typography>
|
|
||||||
<List dense>
|
|
||||||
<Typography variant="p" >
|
|
||||||
To Do:
|
|
||||||
</Typography>
|
|
||||||
<ListItem>
|
|
||||||
<ListItemIcon><ArrowRightIcon /></ListItemIcon>
|
|
||||||
Run a marching square implementation to generate a mesh for a
|
|
||||||
chunk based on test data.
|
|
||||||
</ListItem>
|
|
||||||
<ListItem>
|
|
||||||
<ListItemIcon><ArrowRightIcon /></ListItemIcon>
|
|
||||||
Handle loading chunks asynchronously as the game runs.
|
|
||||||
</ListItem>
|
|
||||||
<ListItem>
|
|
||||||
<ListItemIcon><ArrowRightIcon /></ListItemIcon>
|
|
||||||
Add a noise algorithm to vary the cityscape marching square
|
|
||||||
generation. (Looking at
|
|
||||||
<Link
|
|
||||||
component="span"
|
|
||||||
rel="noopener"
|
|
||||||
href="https://github.com/Aubrrn/FastNoise2"
|
|
||||||
> FastNoise2 </Link>
|
|
||||||
)
|
|
||||||
</ListItem>
|
|
||||||
<ListItem>
|
|
||||||
<ListItemIcon><ArrowRightIcon /></ListItemIcon>
|
|
||||||
Create a mesh data library to make the marching square
|
|
||||||
algorithm generate something that looks like a city. (May
|
|
||||||
require a method of importing models)
|
|
||||||
</ListItem>
|
|
||||||
<ListItem>
|
|
||||||
<ListItemIcon><ArrowRightIcon /></ListItemIcon>
|
|
||||||
Design and create an interesting movement system for the
|
|
||||||
world created. (May need a physics engine like
|
|
||||||
<Link
|
|
||||||
component="span"
|
|
||||||
rel="noopener"
|
|
||||||
href="https://github.com/bulletphysics/bullet3"
|
|
||||||
> Bullet Engine </Link>
|
|
||||||
)
|
|
||||||
</ListItem>
|
|
||||||
<ListItem>
|
|
||||||
<ListItemIcon><ArrowRightIcon /></ListItemIcon>
|
|
||||||
Look into the potential implementation of mono-rails and
|
|
||||||
suspended trains as a stretch goal.
|
|
||||||
</ListItem>
|
|
||||||
</List>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
<p />
|
|
||||||
<Grid container spacing={3}>
|
|
||||||
{this.content.reverse().map((project) => {
|
|
||||||
return (
|
|
||||||
<Grid item xs="12" lg="6">
|
|
||||||
<Card>
|
|
||||||
<CardHeader
|
|
||||||
title={project.header}
|
|
||||||
subheader={project.subheader}
|
|
||||||
/>
|
|
||||||
<CardMedia
|
|
||||||
component={project.mediaType}
|
|
||||||
image={project.image}
|
|
||||||
alt={project.imageAltText}
|
|
||||||
muted
|
|
||||||
loop
|
|
||||||
autoplay
|
|
||||||
controls
|
|
||||||
/>
|
|
||||||
<CardContent>
|
|
||||||
<Typography variant="body1" >
|
|
||||||
{project.content}
|
|
||||||
</Typography>
|
|
||||||
</CardContent>
|
|
||||||
<CardActions>
|
|
||||||
<Button
|
|
||||||
target="_blank"
|
|
||||||
href={project.buttonLink}
|
|
||||||
color="primary"
|
|
||||||
>
|
|
||||||
{project.buttonText}
|
|
||||||
</Button>
|
|
||||||
</CardActions>
|
|
||||||
</Card>
|
|
||||||
</Grid>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Grid>
|
|
||||||
</Container>
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
export default GraphicsBlog;
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
import { h, render, Component } from "preact";
|
|
||||||
import {
|
|
||||||
Container,
|
|
||||||
Card,
|
|
||||||
CardActions,
|
|
||||||
CardContent,
|
|
||||||
Typography,
|
|
||||||
Button,
|
|
||||||
} from "@material-ui/core";
|
|
||||||
const CV = new URL("../static/cv.pdf", import.meta.url);
|
|
||||||
|
|
||||||
import VideoPlayer from "./VideoPlayer.jsx";
|
|
||||||
|
|
||||||
class Greeting extends Component {
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
position: "absolute",
|
|
||||||
width: "100%",
|
|
||||||
left: "49%",
|
|
||||||
right: "49%",
|
|
||||||
top: "30%",
|
|
||||||
height: "60%",
|
|
||||||
objectFit: "cover",
|
|
||||||
transform: "translate(-49%,-50%)",
|
|
||||||
zIndex: "-1",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<VideoPlayer />
|
|
||||||
</div>
|
|
||||||
<Container
|
|
||||||
maxWidth="sm"
|
|
||||||
style={{
|
|
||||||
zindex: "0",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<p />
|
|
||||||
<Card>
|
|
||||||
<CardContent>
|
|
||||||
<Typography variant="h1">Welcome</Typography>
|
|
||||||
<Typography variant="body1">
|
|
||||||
Hi, My name is Warwick and I'm an Associate Lecturer in
|
|
||||||
Computing at the Games Academy in Falmouth University, with an
|
|
||||||
academeic background in Computing for Games and
|
|
||||||
Entrepreneurship. I'm currently looking for oppertunities which
|
|
||||||
would allow me to explore my interests whether thats graphics
|
|
||||||
programming and simulation, or developing and even deeper
|
|
||||||
understanding of linux based technologies.
|
|
||||||
</Typography>
|
|
||||||
</CardContent>
|
|
||||||
<CardActions>
|
|
||||||
<Button href={CV} color="secondary">
|
|
||||||
My Corriculum Vitae
|
|
||||||
</Button>
|
|
||||||
</CardActions>
|
|
||||||
</Card>
|
|
||||||
</Container>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Greeting;
|
|
||||||
|
|
@ -1,74 +0,0 @@
|
||||||
import { h, render, Component } from "preact";
|
|
||||||
import {
|
|
||||||
AppBar,
|
|
||||||
Toolbar,
|
|
||||||
Container,
|
|
||||||
Typography,
|
|
||||||
Button,
|
|
||||||
Box,
|
|
||||||
Link,
|
|
||||||
} from "@material-ui/core";
|
|
||||||
import { makeStyles } from "@material-ui/core/styles";
|
|
||||||
const CV = new URL("../static/cv.pdf", import.meta.url);
|
|
||||||
|
|
||||||
class Header extends Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.styles = makeStyles({
|
|
||||||
toolbarButtons: {
|
|
||||||
marginLeft: "auto",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const classes = this.styles();
|
|
||||||
return (
|
|
||||||
<AppBar position="sticky">
|
|
||||||
<Toolbar>
|
|
||||||
<Link color="inherit" href="/">
|
|
||||||
<Typography variant='h5' component='h1' style={{ flex: 1 }}>
|
|
||||||
Warwick New
|
|
||||||
</Typography>
|
|
||||||
</Link>
|
|
||||||
<Box />
|
|
||||||
<span style={{ marginLeft: "auto" }} >
|
|
||||||
<Button
|
|
||||||
variant="text"
|
|
||||||
href="/graphics-blog"
|
|
||||||
style={{ color: "#f0ffd6" }}
|
|
||||||
>
|
|
||||||
Graphics Blog
|
|
||||||
</Button>
|
|
||||||
{" "}
|
|
||||||
<Button
|
|
||||||
variant="text"
|
|
||||||
href="/research"
|
|
||||||
style={{ color: "#f0ffd6" }}
|
|
||||||
>
|
|
||||||
Research Contributions
|
|
||||||
</Button>
|
|
||||||
{" "}
|
|
||||||
<Button
|
|
||||||
variant="text"
|
|
||||||
href={CV}
|
|
||||||
style={{ color: "#f0ffd6" }}
|
|
||||||
>
|
|
||||||
CV
|
|
||||||
</Button>
|
|
||||||
{" "}
|
|
||||||
<Button
|
|
||||||
variant="text"
|
|
||||||
href="/contact"
|
|
||||||
style={{ color: "#f0ffd6" }}
|
|
||||||
>
|
|
||||||
Contact Me
|
|
||||||
</Button>
|
|
||||||
</span>
|
|
||||||
</Toolbar>
|
|
||||||
</AppBar>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Header;
|
|
||||||
25
src/Home.jsx
|
|
@ -1,25 +0,0 @@
|
||||||
import { h, render, Component } from "preact";
|
|
||||||
import { Container } from "@material-ui/core";
|
|
||||||
import Header from "./Header";
|
|
||||||
import Greeting from "./Greeting";
|
|
||||||
import WebProjects from "./WebProjects";
|
|
||||||
import GameProjects from "./GameProjects";
|
|
||||||
|
|
||||||
class Home extends Component {
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<span>
|
|
||||||
<Header />
|
|
||||||
<Greeting />
|
|
||||||
<div style={{ "margin-top": "8em" }} />
|
|
||||||
<GameProjects />
|
|
||||||
<Container maxWidth="md">
|
|
||||||
<WebProjects />
|
|
||||||
</Container>
|
|
||||||
<p />
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Home;
|
|
||||||
152
src/Monq.jsx
|
|
@ -1,152 +0,0 @@
|
||||||
import { h, render, Component } from "preact";
|
|
||||||
import Header from "./Header";
|
|
||||||
import {
|
|
||||||
Button,
|
|
||||||
Container,
|
|
||||||
Card,
|
|
||||||
CardHeader,
|
|
||||||
CardContent,
|
|
||||||
Typography,
|
|
||||||
Grid,
|
|
||||||
Box
|
|
||||||
} from "@material-ui/core";
|
|
||||||
|
|
||||||
|
|
||||||
const tigerPicture = new URL("../static/images/MonqPage/TigerAI.png", import.meta.url);
|
|
||||||
const tigerDiagram = new URL("../static/images/MonqPage/TigerDiagram.png", import.meta.url);
|
|
||||||
const sandPicture = new URL("../static/images/MonqPage/Sand.png", import.meta.url);
|
|
||||||
const mirrorPicture = new URL("../static/images/MonqPage/MirrorReflection.png", import.meta.url);
|
|
||||||
|
|
||||||
class Monq extends Component {
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<span>
|
|
||||||
<Header />
|
|
||||||
<p />
|
|
||||||
<Container maxWidth="md">
|
|
||||||
<Card>
|
|
||||||
<CardHeader title="Monq" />
|
|
||||||
<CardContent>
|
|
||||||
<Typography variant="p">
|
|
||||||
This is my most complete game project. I made it in my second
|
|
||||||
year of university with some friends, and we were going to use
|
|
||||||
it to form a successful startup. I learned a lot about how a
|
|
||||||
multidisciplinary team works and tinkered with creating
|
|
||||||
frameworks in C++ with inheritance both to blueprints and
|
|
||||||
further C++ sub-classes, covering concepts such as all the AI
|
|
||||||
using unreal's tools, puzzle implementation, shader
|
|
||||||
implementation using noise and more. Most of the mechanics for
|
|
||||||
which I was responsible, used a variety of vector and
|
|
||||||
positional mathematics to implement.
|
|
||||||
</Typography>
|
|
||||||
<p />
|
|
||||||
<Typography variant="h5"> Project and Team </Typography>
|
|
||||||
<Grid container spacing={2}>
|
|
||||||
<Grid item sm={6} xs={12}>
|
|
||||||
<Typography variant="h6"> Programming: </Typography>
|
|
||||||
<Typography variant="p"> Warwick New, </Typography>
|
|
||||||
<Typography variant="p"> James Hellman </Typography>
|
|
||||||
|
|
||||||
<Typography variant="h6"> Designers: </Typography>
|
|
||||||
<Typography variant="p"> James Hellman, </Typography>
|
|
||||||
<Typography variant="p"> Nicholas Stankowski </Typography>
|
|
||||||
|
|
||||||
<Typography variant="h6"> Art: </Typography>
|
|
||||||
<Typography variant="p"> Harri Slee, </Typography>
|
|
||||||
<Typography variant="p"> Robert Adolfson </Typography>
|
|
||||||
|
|
||||||
<Typography variant="h6"> Music: </Typography>
|
|
||||||
<Typography variant="p"> Jason Read </Typography>
|
|
||||||
|
|
||||||
<Typography variant="h6"> Writing: </Typography>
|
|
||||||
<Typography variant="p"> Michael Wheatley </Typography>
|
|
||||||
|
|
||||||
<Typography variant="h6"> Animation: </Typography>
|
|
||||||
<Typography variant="p"> George Evans, </Typography>
|
|
||||||
<Typography variant="p"> Ross Everson </Typography>
|
|
||||||
</Grid>
|
|
||||||
<Grid item sm={6}>
|
|
||||||
<iframe
|
|
||||||
style={{ display: "block", margin: "auto", float: "right" }}
|
|
||||||
width="560" height="315"
|
|
||||||
src="https://www.youtube-nocookie.com/embed/fRSD64D0LUw"
|
|
||||||
title="YouTube video player" frameborder="0"
|
|
||||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media;
|
|
||||||
gyroscope; picture-in-picture" allowfullscreen>
|
|
||||||
</iframe>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
<p />
|
|
||||||
<iframe
|
|
||||||
style={{ display: "block", margin: "0 auto" }}
|
|
||||||
src="https://itch.io/embed/131406" height="167" width="552"
|
|
||||||
frameborder="0">
|
|
||||||
<a
|
|
||||||
href="https://d-tail-entertainment.itch.io/monq">MONQ by
|
|
||||||
dtailent
|
|
||||||
</a>
|
|
||||||
</iframe>
|
|
||||||
<p />
|
|
||||||
<Typography variant="h5"> Stuff I worked on </Typography>
|
|
||||||
<Typography variant="h6"> Tiger AI </Typography>
|
|
||||||
<Grid container spacing={2}>
|
|
||||||
<Grid item sm={6} xs={12}>
|
|
||||||
Here I created an AI that guards an arena. The puzzle part
|
|
||||||
of this boss was based on trying to get the ai to get a
|
|
||||||
chain stuck on pillars left around the arena reducing the
|
|
||||||
range it could navigate. I did this by calculating the
|
|
||||||
distance between the previous attachment point and the next
|
|
||||||
and reducing the available navigatable circumference of the
|
|
||||||
tiger appropriately. Since this was my first more
|
|
||||||
complicated AI, I now know that It would have been far
|
|
||||||
easier to restrict the AI's movement through the use of a
|
|
||||||
more complex steering system rather than calculating where
|
|
||||||
the actor should target moving towards.
|
|
||||||
</Grid>
|
|
||||||
<Grid item sm={6}>
|
|
||||||
<Box
|
|
||||||
component="img"
|
|
||||||
alt="Image of Tiger AI"
|
|
||||||
src={tigerPicture}
|
|
||||||
style={{ width: "100%", "float": "right" }}
|
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
<Box
|
|
||||||
component="img"
|
|
||||||
alt="Diagram of Tiger AI"
|
|
||||||
src={tigerDiagram}
|
|
||||||
style={{ width: "100%", "float": "right" }}
|
|
||||||
/>
|
|
||||||
<Typography variant="h6"> Mirror Reflections </Typography>
|
|
||||||
<Grid container spacing={2}>
|
|
||||||
<Grid item sm={6}>
|
|
||||||
<Box
|
|
||||||
component="img"
|
|
||||||
alt="Image of mirror reflection"
|
|
||||||
src={mirrorPicture}
|
|
||||||
style={{ width: "100%", "float": "left" }}
|
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
<Grid item sm={6} xs={12}>
|
|
||||||
Here is an example of me simulating reflections in monq to
|
|
||||||
create a light bounce puzzle. Each mirror triggers the next
|
|
||||||
to recursively make light reflection beams. Here I had to
|
|
||||||
work out angles of reflection before creating a new cast to
|
|
||||||
attach a beam particle.
|
|
||||||
<p />
|
|
||||||
I also worked on the sand texture in this image mixing
|
|
||||||
various forms of noise to create the ripples based on
|
|
||||||
changes in topography.
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
<p />
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</Container>
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Monq;
|
|
||||||
|
|
@ -1,70 +0,0 @@
|
||||||
import { h, render, Component } from "preact";
|
|
||||||
import Header from "./Header";
|
|
||||||
import {
|
|
||||||
Container,
|
|
||||||
Card,
|
|
||||||
CardHeader,
|
|
||||||
CardContent,
|
|
||||||
Typography,
|
|
||||||
} from "@material-ui/core";
|
|
||||||
const rambleDemoWebm = new URL("../static/video/ramble_demo/ramble.webm", import.meta.url);
|
|
||||||
const rambleDemoMp4 = new URL("../static/video/ramble_demo/ramble.mp4", import.meta.url);
|
|
||||||
|
|
||||||
class Ramble extends Component {
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<span>
|
|
||||||
<Header />
|
|
||||||
<p />
|
|
||||||
<Container maxWidth="md">
|
|
||||||
<Card>
|
|
||||||
<CardHeader title="Ramble" />
|
|
||||||
<CardContent>
|
|
||||||
<Typography variant="p">
|
|
||||||
Ramble was a web project I created during my master's degree to
|
|
||||||
create a startup. In this project, I was the CTO and built a
|
|
||||||
website that allowed users to stream their podcasts live and
|
|
||||||
accept call-ins in a similar vein to talk shows! I learnt the
|
|
||||||
entire javascript web development stack from React to audio
|
|
||||||
streaming to DevOps in order to make it a reality.
|
|
||||||
</Typography>
|
|
||||||
<p />
|
|
||||||
<Typography variant="p">
|
|
||||||
It also had chatroom functionality with the ability to
|
|
||||||
re-stream to audiences much like other apps that came out in
|
|
||||||
the time since, such as Clubhouse.
|
|
||||||
</Typography>
|
|
||||||
<p />
|
|
||||||
<Typography variant="p">
|
|
||||||
During the project's lifespan of just under two years, I mostly
|
|
||||||
worked on creating the streaming functionality of the project
|
|
||||||
and managing how the project was designed to function behind
|
|
||||||
the hood when it was deployed. I learned a ton about how web
|
|
||||||
deployment works during this time allowing me to work on web
|
|
||||||
development modules in the Games Academy at Falmouth University
|
|
||||||
today.
|
|
||||||
</Typography>
|
|
||||||
<p />
|
|
||||||
<Typography variant="p">
|
|
||||||
Here's a demo of the project I took before we moved on.
|
|
||||||
</Typography>
|
|
||||||
<p />
|
|
||||||
<div>
|
|
||||||
<video controls loop muted autoPlay={true} style={{
|
|
||||||
width: "100%",
|
|
||||||
height: "auto",
|
|
||||||
objectFit: "cover",
|
|
||||||
}}>
|
|
||||||
<source src={rambleDemoMp4} type="video/mp4" />
|
|
||||||
<source src={rambleDemoWebm} type="video/webm" />
|
|
||||||
</video>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</Container>
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Ramble;
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
import { h, render, Component } from "preact";
|
|
||||||
import Header from "./Header";
|
|
||||||
import {
|
|
||||||
Container,
|
|
||||||
Card,
|
|
||||||
CardHeader,
|
|
||||||
CardContent,
|
|
||||||
Typography,
|
|
||||||
List,
|
|
||||||
ListItem,
|
|
||||||
ListItemText,
|
|
||||||
Link,
|
|
||||||
} from "@material-ui/core";
|
|
||||||
|
|
||||||
class Contact extends Component {
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<span>
|
|
||||||
<Header />
|
|
||||||
<Container maxWidth="md">
|
|
||||||
<p />
|
|
||||||
<Card>
|
|
||||||
<CardHeader title="Research Contributions" />
|
|
||||||
<CardContent>
|
|
||||||
<ListItemText>
|
|
||||||
An Exploratory Analysis of Student Experiences with Peer Evaluation in Group Game Development Projects:{" "}
|
|
||||||
<p>
|
|
||||||
<Link
|
|
||||||
rel="noopener"
|
|
||||||
href="https://dl.acm.org/doi/10.1145/3555009.3555021"
|
|
||||||
>
|
|
||||||
https://dl.acm.org/doi/10.1145/3555009.3555021
|
|
||||||
</Link>
|
|
||||||
</p>
|
|
||||||
</ListItemText>
|
|
||||||
<ListItemText>
|
|
||||||
Student Perspectives on the Purpose of Peer Evaluation During Group Game Development Projects:{" "}
|
|
||||||
<p>
|
|
||||||
<Link
|
|
||||||
rel="noopener"
|
|
||||||
href="https://dl.acm.org/doi/10.1145/3481282.3481294"
|
|
||||||
>
|
|
||||||
https://dl.acm.org/doi/10.1145/3481282.3481294
|
|
||||||
</Link>
|
|
||||||
</p>
|
|
||||||
</ListItemText>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</Container>
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Contact;
|
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
import { h, render, Component } from "preact";
|
|
||||||
import Header from "./Header";
|
|
||||||
import {
|
|
||||||
Button,
|
|
||||||
Container,
|
|
||||||
Card,
|
|
||||||
CardHeader,
|
|
||||||
CardContent,
|
|
||||||
Typography,
|
|
||||||
} from "@material-ui/core";
|
|
||||||
const graphicsDemoMp4 = new URL("../static/video/uni_graphics_demo/uni-graphics.mp4", import.meta.url);
|
|
||||||
const graphicsDemoBuild = new URL("../static/builds/UniGraphics-Win64.zip", import.meta.url);
|
|
||||||
|
|
||||||
class UniGraphics extends Component {
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<span>
|
|
||||||
<Header />
|
|
||||||
<p />
|
|
||||||
<Container maxWidth="md">
|
|
||||||
<Card>
|
|
||||||
<CardHeader title="University Graphics Project" />
|
|
||||||
<CardContent>
|
|
||||||
<Typography variant="p">
|
|
||||||
I made this project for a module at uni. It's a terrain map
|
|
||||||
based on Perlin noise with flat face lighting, It was the first
|
|
||||||
project where I picked up a lot of the 3D maths used in games
|
|
||||||
and learned about simulation. It was my first project using
|
|
||||||
GLSL with C++ and also the project I optimised for an
|
|
||||||
optimiseation module in my final year. Getting deep into
|
|
||||||
learning more about memory management and Object-Oriented
|
|
||||||
design.
|
|
||||||
</Typography>
|
|
||||||
<p />
|
|
||||||
<Typography variant="p">
|
|
||||||
Here's a build and a video demo:  
|
|
||||||
<Button
|
|
||||||
variant="contained"
|
|
||||||
color="primary"
|
|
||||||
href={graphicsDemoBuild}
|
|
||||||
>Win64-Build.zip</Button>
|
|
||||||
</Typography>
|
|
||||||
<p />
|
|
||||||
<div>
|
|
||||||
<video controls loop muted autoPlay={true} style={{
|
|
||||||
width: "100%",
|
|
||||||
height: "auto",
|
|
||||||
objectFit: "cover",
|
|
||||||
}}>
|
|
||||||
<source src={graphicsDemoMp4} type="video/mp4" />
|
|
||||||
</video>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</Container>
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default UniGraphics;
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
import { h, render, Component } from "preact";
|
|
||||||
//import React from "react";
|
|
||||||
import Hls from "hls.js";
|
|
||||||
const PortfolioVideo = new URL("../static/video/background_video/index.m3u8", import.meta.url);
|
|
||||||
export default class VideoPlayer extends Component {
|
|
||||||
state = {};
|
|
||||||
// componentDidUpdate() {
|
|
||||||
componentDidMount() {
|
|
||||||
const video = this.player;
|
|
||||||
const url = PortfolioVideo.href; //"https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8";
|
|
||||||
if (Hls.isSupported()) {
|
|
||||||
const hls = new Hls();
|
|
||||||
|
|
||||||
hls.loadSource(url);
|
|
||||||
hls.attachMedia(video);
|
|
||||||
hls.on(Hls.Events.MANIFEST_PARSED, function() {
|
|
||||||
video.play();
|
|
||||||
});
|
|
||||||
} else if (video.canPlayType("application/vnd.apple.mpegurl")) {
|
|
||||||
video.src = url;
|
|
||||||
video.addEventListener("loadedmetadata", function() {
|
|
||||||
video.play();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<video
|
|
||||||
style={{
|
|
||||||
position: "absolute",
|
|
||||||
width: "100%",
|
|
||||||
height: "100%",
|
|
||||||
objectFit: "cover",
|
|
||||||
}}
|
|
||||||
loop
|
|
||||||
muted
|
|
||||||
className="videoCanvas"
|
|
||||||
ref={(player) => (this.player = player)}
|
|
||||||
autoPlay={true}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
import { h, render, Component } from "preact";
|
|
||||||
import {
|
|
||||||
Container,
|
|
||||||
Paper,
|
|
||||||
Card,
|
|
||||||
CardHeader,
|
|
||||||
CardMedia,
|
|
||||||
CardActions,
|
|
||||||
CardContent,
|
|
||||||
Typography,
|
|
||||||
Button,
|
|
||||||
} from "@material-ui/core";
|
|
||||||
import RambleScreenshot from "../static/images/RambleScreenshot.png";
|
|
||||||
class WebProjects extends Component {
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<Container>
|
|
||||||
<p />
|
|
||||||
<Card>
|
|
||||||
<CardHeader title="Web Development Projects" />
|
|
||||||
</Card>
|
|
||||||
<p />
|
|
||||||
<Card>
|
|
||||||
<CardHeader
|
|
||||||
title="ramble.fm"
|
|
||||||
subheader="Ramble Media LTD - Fullstack Web Developer: ReactJS,
|
|
||||||
NodeJS, Janus Media Server, Docker..."
|
|
||||||
/>
|
|
||||||
<CardMedia
|
|
||||||
style={{ height: 0, paddingTop: "25%" }}
|
|
||||||
image={RambleScreenshot}
|
|
||||||
title="ramble.fm"
|
|
||||||
/>
|
|
||||||
<CardContent>
|
|
||||||
<Typography variant="body1">
|
|
||||||
In this project, I was the CTO and built a website that allowed
|
|
||||||
users to stream their podcasts live and accept call-ins in a
|
|
||||||
similar vein to talk shows! I learnt the entire javascript web
|
|
||||||
development stack from React to audio streaming to DevOps in
|
|
||||||
order to make it a reality.
|
|
||||||
</Typography>
|
|
||||||
</CardContent>
|
|
||||||
<CardActions>
|
|
||||||
<Button href="/ramble" color="primary">
|
|
||||||
Learn More
|
|
||||||
</Button>
|
|
||||||
</CardActions>
|
|
||||||
</Card>
|
|
||||||
</Container>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export default WebProjects;
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
import { h, render, Component } from "preact";
|
|
||||||
import Router from "preact-router";
|
|
||||||
import AsyncRoute from "preact-async-route";
|
|
||||||
import { ThemeProvider } from "@material-ui/core/styles";
|
|
||||||
import CssBaseline from "@material-ui/core/CssBaseline";
|
|
||||||
import Background from "./Background"
|
|
||||||
import Home from "./Home";
|
|
||||||
import theme from "./theme";
|
|
||||||
import "fontsource-roboto";
|
|
||||||
|
|
||||||
class App extends Component {
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<ThemeProvider theme={theme}>
|
|
||||||
<CssBaseline />
|
|
||||||
<Router>
|
|
||||||
<Home path="/" />
|
|
||||||
<AsyncRoute path="/research" getComponent={() =>
|
|
||||||
import("./Research").then((module) => module.default)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<AsyncRoute path="/contact" getComponent={() =>
|
|
||||||
import("./Contact").then((module) => module.default)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<AsyncRoute path="/ramble" getComponent={() =>
|
|
||||||
import("./Ramble").then((module) => module.default)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<AsyncRoute path="/monq" getComponent={() =>
|
|
||||||
import("./Monq").then((module) => module.default)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<AsyncRoute path="/uni-graphics" getComponent={() =>
|
|
||||||
import("./UniGraphics").then((module) => module.default)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<AsyncRoute path="/graphics-blog" getComponent={() =>
|
|
||||||
import("./GraphicsBlog").then((module) => module.default)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<AsyncRoute default getComponent={() =>
|
|
||||||
import("./404Page").then((module) => module.default)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Router>
|
|
||||||
<Background />
|
|
||||||
</ThemeProvider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render(<App />, document.body);
|
|
||||||
36
src/theme.js
|
|
@ -1,36 +0,0 @@
|
||||||
import { createTheme } from "@material-ui/core/styles";
|
|
||||||
|
|
||||||
const bg_0h = { main: "#1d2021", contrastText: "#282828" };
|
|
||||||
const bg = { main: "#282828", contrastText: "#282828" };
|
|
||||||
const red = { main: "#fb4934", contrastText: "#282828" };
|
|
||||||
const green = { main: "#b8bb26", contrastText: "#282828" };
|
|
||||||
const yellow = { main: "#fabd2f", contrastText: "#282828" };
|
|
||||||
const blue = { main: "#83a598", contrastText: "#282828" };
|
|
||||||
const purple = { main: "#d3869b", contrastText: "#282828" };
|
|
||||||
const aqua = { main: "#8ec07c", contrastText: "#282828" };
|
|
||||||
const gray = { main: "#a89984", contrastText: "#282828" };
|
|
||||||
const orange = { main: "#8ec07c", contrastText: "#282828" };
|
|
||||||
const fg = { main: "#ebdbb2", contrastText: "#282828" };
|
|
||||||
|
|
||||||
const theme = createTheme({
|
|
||||||
palette: {
|
|
||||||
type: 'light',
|
|
||||||
primary: {
|
|
||||||
main: '#00a878',
|
|
||||||
},
|
|
||||||
secondary: {
|
|
||||||
main: '#046865',
|
|
||||||
},
|
|
||||||
background: {
|
|
||||||
default: '#f0ffd6',
|
|
||||||
},
|
|
||||||
error: {
|
|
||||||
main: '#e53d00',
|
|
||||||
},
|
|
||||||
warning: {
|
|
||||||
main: '#ffe900',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default theme;
|
|
||||||
BIN
static/cv.pdf
|
Before Width: | Height: | Size: 1 MiB |
|
Before Width: | Height: | Size: 126 KiB |
|
Before Width: | Height: | Size: 215 KiB |
|
Before Width: | Height: | Size: 204 KiB |
|
Before Width: | Height: | Size: 403 KiB |
|
Before Width: | Height: | Size: 248 KiB |
|
Before Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 692 KiB |
|
Before Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 144 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
|
@ -1,86 +0,0 @@
|
||||||
// create background element
|
|
||||||
let canvas = document.createElement("canvas");
|
|
||||||
document.body.appendChild(canvas);
|
|
||||||
canvas.style.cssText = `
|
|
||||||
background: #f0ffd6;
|
|
||||||
position:fixed;
|
|
||||||
left:0;
|
|
||||||
top:0;
|
|
||||||
z-index:-99999;
|
|
||||||
`
|
|
||||||
// set it's size
|
|
||||||
canvas.width = window.innerWidth;
|
|
||||||
canvas.height = window.innerHeight;
|
|
||||||
|
|
||||||
let context = canvas.getContext('2d');
|
|
||||||
|
|
||||||
// this will be populated and used later
|
|
||||||
drawableObjectList = [];
|
|
||||||
|
|
||||||
// this is the object that'll look cool
|
|
||||||
class Point {
|
|
||||||
constructor() {
|
|
||||||
this.x = Math.random() * window.innerWidth;
|
|
||||||
this.y = Math.random() * window.innerHeight;
|
|
||||||
|
|
||||||
this.dx = (Math.random()) - 0.5;
|
|
||||||
this.dy = (Math.random()) - 0.5;
|
|
||||||
}
|
|
||||||
draw() {
|
|
||||||
context.fillStyle = "#00a878"
|
|
||||||
context.strokeStyle = "#00a878"
|
|
||||||
// find and draw links
|
|
||||||
drawableObjectList.forEach((point) => {
|
|
||||||
if (point.x > this.x - 100 && point.x < this.x + 100 &&
|
|
||||||
point.y > this.y - 100 && point.y < this.y + 100) {
|
|
||||||
context.beginPath()
|
|
||||||
context.moveTo(this.x, this.y)
|
|
||||||
context.lineTo(point.x, point.y)
|
|
||||||
context.stroke();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// draw point
|
|
||||||
context.beginPath();
|
|
||||||
context.arc(this.x, this.y, 3, 0, Math.PI * 2, false);
|
|
||||||
context.fill();
|
|
||||||
context.stroke();
|
|
||||||
|
|
||||||
// move point
|
|
||||||
this.x += this.dx;
|
|
||||||
this.y += this.dy;
|
|
||||||
|
|
||||||
// keep point in bounds
|
|
||||||
if (this.x > window.innerWidth || this.x < 0) {
|
|
||||||
this.dx *= -1
|
|
||||||
}
|
|
||||||
if (this.y > window.innerHeight || this.y < 0) {
|
|
||||||
this.dy *= -1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// populate element array
|
|
||||||
for (let i = 0; i < 100; i++) {
|
|
||||||
drawableObjectList.push(new Point());
|
|
||||||
}
|
|
||||||
|
|
||||||
function loop() {
|
|
||||||
// Loop and clear frame
|
|
||||||
requestAnimationFrame(loop);
|
|
||||||
context.clearRect(0, 0, window.innerWidth, window.innerHeight);
|
|
||||||
|
|
||||||
// Draw objects.
|
|
||||||
drawableObjectList.forEach((point) => {
|
|
||||||
point.draw();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Handle page size change
|
|
||||||
if (canvas.width != window.innerWidth) {
|
|
||||||
canvas.width = window.innerWidth;
|
|
||||||
}
|
|
||||||
if (canvas.height != window.innerHeight) {
|
|
||||||
canvas.height = window.innerHeight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
loop();
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
#EXTM3U
|
|
||||||
#EXT-X-VERSION:3
|
|
||||||
#EXT-X-TARGETDURATION:17
|
|
||||||
#EXT-X-MEDIA-SEQUENCE:0
|
|
||||||
#EXTINF:11.233333,
|
|
||||||
index0.ts
|
|
||||||
#EXTINF:9.466667,
|
|
||||||
index1.ts
|
|
||||||
#EXTINF:16.666667,
|
|
||||||
index2.ts
|
|
||||||
#EXTINF:8.333333,
|
|
||||||
index3.ts
|
|
||||||
#EXTINF:8.333333,
|
|
||||||
index4.ts
|
|
||||||
#EXTINF:6.433333,
|
|
||||||
index5.ts
|
|
||||||
#EXTINF:0.200000,
|
|
||||||
index6.ts
|
|
||||||
#EXT-X-ENDLIST
|
|
||||||