Skip to content

Instantly share code, notes, and snippets.

@Manasse228
Created May 24, 2022 17:36
Show Gist options
  • Save Manasse228/7a8f557e7915a99031b80e050ca2fed5 to your computer and use it in GitHub Desktop.
Save Manasse228/7a8f557e7915a99031b80e050ca2fed5 to your computer and use it in GitHub Desktop.
Terrain Editor
<div id="threejs"></div>
var camera, scene, renderer, light, rectangles = [];
var raycaster, INTERSECTED;
var mouse, controls, stats;
var container;
var MOUSEDOWN, MOUSEUP, MOUSEBTN;
var RECT_SIZE = 15,
RECT_HEIGHT = 256,
GRID_SIZE = 27;
var NUM_CUBES = GRID_SIZE * GRID_SIZE;
var SHADOW_MAP_WIDTH = 2048,
SHADOW_MAP_HEIGHT = 1024;
var guiOption = function () {
this.color0 = "#ffae23";
this.viewMode = 'Orbit Mode';
this.brushType = 'Gaussian';
this.brushSize = 1.0;
this.brushMagnitude = 30;
this.blurMagnitude = 0.13;
this.orbitViewRadius = 400;
this.orbitViewTheta = 0;
this.reset = function () {
reset();
};
this.toggleShadow = function () {
castShadow();
};
this.castShadow = true;
this.singleColor = function () {
setSingleColor();
};
this.randomColor = function () {
setRandomColor();
};
};
var options = new guiOption();
$.ready(main());
function main() {
setUpGUI();
setUpStats(document.body);
init();
animate();
}
function init() {
MOUSEDOWN = false;
MOUSEUP = true;
container = document.getElementById("threejs");
document.body.appendChild(container);
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setClearColor(0xf0f0f0);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.sortObjects = false;
renderer.shadowMapEnabled = true;
renderer.shadowMapType = THREE.PCFShadowMap;
container.appendChild(renderer.domElement);
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.z = 400;
scene = new THREE.Scene();
mouse = new THREE.Vector2();
raycaster = new THREE.Raycaster();
controls = new THREE.OrbitControls(camera, renderer.domElement);
rectangles = [];
// LIGHTS
var ambient = new THREE.AmbientLight(0x444444);
scene.add(ambient);
light = new THREE.SpotLight(0xffffff, 1, 0, Math.PI / 2, 1);
light.position.set(0, 1500, 1000);
light.target.position.set(0, 0, 0);
light.castShadow = true;
light.shadowCameraNear = 1200;
light.shadowCameraFar = 2500;
light.shadowCameraFov = 50;
light.shadowBias = 0.0001;
light.shadowDarkness = 0.5;
light.shadowMapWidth = SHADOW_MAP_WIDTH;
light.shadowMapHeight = SHADOW_MAP_HEIGHT;
scene.add(light);
populateRectangles(scene);
showCaseRectangles();
document.addEventListener('keydown', onDocumentKeyDown, false);
document.addEventListener('mousemove', onDocumentMouseMove, false);
document.addEventListener('mousedown', onDocumentMouseDown, false);
document.addEventListener('mouseup', onDocumentMouseUp, false);
window.addEventListener('resize', onWindowResize, false);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function animate() {
requestAnimationFrame(animate);
stats.begin();
render();
stats.end();
}
function render() {
// View Mode
switch (options.viewMode) {
case 'Orbital Control':
controls.update();
break;
case 'Orbit Mode':
options.orbitViewTheta += 0.1;
if (options.orbitViewTheta >= 360) {
options.orbitViewTheta = 0;
}
camera.position.x = options.orbitViewRadius * Math.sin(THREE.Math.degToRad(options.orbitViewTheta));
camera.position.y = options.orbitViewRadius;
camera.position.z = options.orbitViewRadius * Math.cos(THREE.Math.degToRad(options.orbitViewTheta));
camera.lookAt(scene.position);
camera.updateMatrixWorld();
break;
}
// Brush Action
if (MOUSEDOWN) {
raycaster.setFromCamera(mouse, camera);
var intersects = raycaster.intersectObjects(scene.children);
if (intersects.length >= 1 && MOUSEBTN == 1) {
INTERSECTED = intersects[0].object;
applyBrush(3);
}
}
renderer.render(scene, camera);
}
function populateRectangles(parent) {
var x = 0;
var z = 0;
var row = 0;
var col = 0;
var rectangle;
var geometry = new THREE.BoxGeometry(RECT_SIZE, RECT_HEIGHT, RECT_SIZE);
for (var i = 0; i < NUM_CUBES; i++) {
var material = new THREE.MeshLambertMaterial({
color: Math.random() * 0xffffff
});
if ((i % GRID_SIZE) === 0) {
col = 1;
row++;
} else {
col++;
}
x = -(((GRID_SIZE * RECT_SIZE) / 2) - ((RECT_SIZE) * col) + (RECT_SIZE / 2));
z = (((GRID_SIZE * RECT_SIZE) / 2) - ((RECT_SIZE) * row) + (RECT_SIZE / 2));
rectangle = new THREE.Mesh(geometry, material);
rectangle.position.set(x, -3000, z);
rectangle.castShadow = options.castShadow;
rectangle.receiveShadow = options.castShadow;
rectangles.push(rectangle);
parent.add(rectangle);
}
}
function onDocumentKeyDown(event) {
switch (event.which) {
case 66:
for (var i = 0; i < rectangles.length; i++) {
TweenMax.to(rectangles[i].position, 1 + Math.random() * 1, {
y: 100,
ease: SteppedEase.config(12 + Math.ceil(Math.random() * 5)),
});
}
break;
case 67:
for (var i = 0; i < rectangles.length; i++) {
TweenMax.to(rectangles[i].position, 1 + Math.random() * 2, {
y: 100,
ease: Elastic.easeOut,
});
}
break;
case 80:
break;
case 86:
var t1 = new TimelineLite();
for (var i = 0; i < rectangles.length; i++) {
t1.to(rectangles[i].position, .005, {
y: 10,
ease: Linear.easeNone,
});
}
t1.play();
break;
case 88:
TweenMax.to(rectangles[0].position, 1, {
y: 10,
ease: Elastic.easeOut,
});
//xrectangles[0].position.y += 20;
break;
}
}
function onDocumentMouseMove(event) {
event.preventDefault();
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
}
function onDocumentMouseDown(event) {
MOUSEUP = false;
MOUSEDOWN = true;
MOUSEBTN = event.which;
}
function onDocumentMouseUp(event) {
MOUSEDOWN = false;
MOUSEUP = true;
}
function applyBrush() {
var x = INTERSECTED.position.x;
var z = INTERSECTED.position.z;
var col = (x + ((GRID_SIZE * RECT_SIZE) / 2) - (RECT_SIZE / 2)) / RECT_SIZE;
var row = ((z - ((GRID_SIZE * RECT_SIZE) / 2) - (RECT_SIZE / 2)) / -RECT_SIZE) - 1;
var index = row * GRID_SIZE + col;
//console.log(col + ' ' + row + ' ' + index);
var sigma = 1;
var strength = 1 / 49;
var width = 3;
switch (options.brushType) {
case 'Gaussian':
gaussianBrush(options.brushSize, options.brushMagnitude, row, col);
break;
case 'Pinpoint':
pinpointBrush(options.brushSize, options.brushMagnitude, row, col);
break;
case 'Blur':
blurBrush(options.blurMagnitude, row, col);
break;
case 'Color':
colorBrush(options.brushSize, options.color0, row, col);
break;
}
}
function gaussianBrush(sigma, magnitude, row, col) {
var fWidth = Math.ceil(sigma * 3);
var fSize = (fWidth * 2) + 1;
var newRow = 0;
var newCol = 0;
var index, fIndex = 0;
var gFilter;
gFilter = createGaussianFilter(sigma, magnitude);
for (var i = -fWidth; i <= fWidth; i++) {
for (var j = -fWidth; j <= fWidth; j++) {
newRow = row + i;
newCol = col + j;
if (newRow >= 0 && newRow < GRID_SIZE && newCol >= 0 && newCol < GRID_SIZE) {
index = (newRow * GRID_SIZE) + newCol;
rectangles[index].position.y += gFilter[fIndex];
}
fIndex += 1;
}
}
}
function pinpointBrush(sigma, magnitude, row, col) {
var fWidth = Math.ceil(sigma * 2);
var fSize = (fWidth * 2) + 1;
var newRow = 0;
var newCol = 0;
var index;
var gFilter;
if (sigma < 0.6) {
index = (row * GRID_SIZE) + col;
rectangles[index].position.y += magnitude;
} else {
for (var i = -fWidth; i <= fWidth; i++) {
for (var j = -fWidth; j <= fWidth; j++) {
newRow = row + i;
newCol = col + j;
if (newRow >= 0 && newRow < GRID_SIZE && newCol >= 0 && newCol < GRID_SIZE) {
index = (newRow * GRID_SIZE) + newCol;
rectangles[index].position.y += magnitude / 6;
}
}
}
}
}
function blurBrush(magnitude, row, col) {
var fWidth = 1;
var fSize = (fWidth * 2) + 1;
var newRow = 0;
var newCol = 0;
var index = (row * GRID_SIZE) + col;
var fIndex = 0;
var gFilter;
var neighbors;
var sum = 0;
for (var i = -fWidth; i <= fWidth; i++) {
for (var j = -fWidth; j <= fWidth; j++) {
newRow = row + i;
newCol = col + j;
if (newRow >= 0 && newRow < GRID_SIZE && newCol >= 0 && newCol < GRID_SIZE) {
findex = (newRow * GRID_SIZE) + newCol;
sum += (rectangles[findex].position.y + 255) * magnitude;
}
}
}
//console.log(sum);
rectangles[index].position.y = sum - 255;
}
function colorBrush(sigma, color, row, col) {
var c = parseHex(color);
var fWidth = Math.ceil(sigma);
var fSize = (fWidth * 2) + 1;
var newRow = 0;
var newCol = 0;
var index;
if (sigma < 1) {
index = (row * GRID_SIZE) + col;
rectangles[index].material.color.setHex(c);
} else {
for (var i = -fWidth; i <= fWidth; i++) {
for (var j = -fWidth; j <= fWidth; j++) {
newRow = row + i;
newCol = col + j;
if (newRow >= 0 && newRow < GRID_SIZE && newCol >= 0 && newCol < GRID_SIZE) {
index = (newRow * GRID_SIZE) + newCol;
rectangles[index].material.color.setHex(c);
}
}
}
}
}
function createGaussianFilter(sigma, magnitude) {
var fWidth = Math.ceil(sigma * 3);
var fSize = (fWidth * 2) + 1;
var gFilter = [];
var temp;
var sum = 0;
for (var x = -fWidth; x <= fWidth; x++) {
for (var y = -fWidth; y <= fWidth; y++) {
temp = Math.exp(-((x * x) + (y * y)) / (2 * (sigma * sigma))) / (2 * Math.PI * (sigma * sigma));
sum += temp;
gFilter.push(temp);
}
}
for (var i = 0; i < gFilter.length; i++) {
gFilter[i] = (gFilter[i] / sum) * magnitude;
}
return gFilter;
}
function castShadow() {
if (options.castShadow) {
options.castShadow = false;
} else {
options.castShadow = true;
}
for (var i = 0; i < rectangles.length; i++) {
rectangles[i].castShadow = options.castShadow;
rectangles[i].recieveShadow = options.castShadow;
}
}
function setSingleColor() {
var c = parseHex(options.color0);
for (var i = 0; i < rectangles.length; i++) {
rectangles[i].material.color.setHex(c);
}
}
function setRandomColor() {
for (var i = 0; i < rectangles.length; i++) {
rectangles[i].material.color.setHex(Math.random() * 0xffffff);
}
}
function showCaseRectangles() {
for (var i = 0; i < rectangles.length; i++) {
TweenMax.to(rectangles[i].position, 1 + Math.random() * 1.5, {
y: -85,
//ease: SteppedEase.config(12 + Math.ceil(Math.random() * 5)),
//ease: Elastic.easeOut,
ease: Power4.easeInOut,
});
TweenMax.to(rectangles[i].position, 1 + Math.random() * 1, {
y: -155,
delay: 3,
ease: Elastic.easeOut,
});
}
}
function reset() {
for (var i = 0; i < rectangles.length; i++) {
TweenMax.to(rectangles[i].position, 1 + Math.random() * 1, {
y: -155,
ease: Elastic.easeOut,
});
}
}
function setUpGUI() {
var gui = new dat.GUI();
var f0 = gui.addFolder('View Mode');
f0.add(options, 'viewMode', ['Orbital Control', 'Orbit Mode', 'Stationary Mode']);
f0.add(options, 'orbitViewTheta', 0, 360).listen();
f0.add(options, 'orbitViewRadius', 100, 500);
var f1 = gui.addFolder('Brush');
f1.add(options, 'brushType', ['Gaussian', 'Pinpoint', 'Blur', 'Color']);
f1.add(options, 'brushSize', .5, 2).onChange(function (value) {
brushSize = value;
});
f1.add(options, 'brushMagnitude', -40, 40).onChange(function (value) {
brushMagnitude = value;
});
f1.add(options, 'blurMagnitude', .10, .15);
var f2 = gui.addFolder('Lighting');
f2.add(options, 'toggleShadow');
var f3 = gui.addFolder('Color');
f3.addColor(options, 'color0').name('Color');
f3.add(options, 'singleColor');
f3.add(options, 'randomColor');
gui.add(options, 'reset').name('Reset');
}
function setUpStats(parent) {
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '0px';
stats.domElement.style.top = '0px';
stats.setMode(0);
parent.appendChild(stats.domElement);
}
function parseHex(hex) {
return '0x' + hex.slice(1);
}
<script src="//cdnjs.cloudflare.com/ajax/libs/three.js/r71/three.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/dat-gui/0.5/dat.gui.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/stats.js/r11/Stats.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.js"></script>
<script src="//cdpn.io/jamwjam/pen/VYoqZj.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/gsap/1.16.1/TweenMax.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/gsap/1.16.1/TimelineLite.min.js"></script>
* {
margin: 0px;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment