mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-17 09:41:24 +01:00
v1.2
This commit is contained in:
parent
93c0c64cbd
commit
f6ec0251ec
11 changed files with 449 additions and 168 deletions
226
modules/ui/3d.js
Normal file
226
modules/ui/3d.js
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
"use strict";
|
||||
let threeD = {}; // master object for 3d scane and parameters
|
||||
let threeDscale = 50; // 3d scene scale
|
||||
|
||||
// start 3d view and heightmap edit preview
|
||||
async function start3d(canvas) {
|
||||
const loaded = await loadTHREE();
|
||||
if (!loaded) {tip("Cannot load 3d library", false, "error", 4000); return false};
|
||||
|
||||
threeD.scene = new THREE.Scene();
|
||||
//threeD.scene.background = new THREE.Color(0x53679f);
|
||||
threeD.camera = new THREE.PerspectiveCamera(70, canvas.width / canvas.height, 0.1, 2000);
|
||||
threeD.camera.position.x = 0;
|
||||
threeD.camera.position.z = 350;
|
||||
threeD.camera.position.y = 285;
|
||||
threeD.Renderer = new THREE.WebGLRenderer({canvas, antialias: true, preserveDrawingBuffer: true});
|
||||
threeD.controls = await OrbitControls(threeD.camera, threeD.Renderer.domElement);
|
||||
threeD.controls.minDistance = 10; threeD.controls.maxDistance = 1000;
|
||||
threeD.controls.maxPolarAngle = Math.PI/2;
|
||||
threeD.controls.keys = {};
|
||||
|
||||
threeD.Renderer.setSize(canvas.width, canvas.height);
|
||||
add3dMesh(graphWidth, graphHeight, grid.cellsX, grid.cellsY);
|
||||
|
||||
const ambientLight = new THREE.AmbientLight(0xcccccc, .7);
|
||||
threeD.scene.add(ambientLight);
|
||||
const spotLight = new THREE.SpotLight(0xcccccc, .8, 2000, .7, 0, 0);
|
||||
spotLight.position.set(100, 600, 1000);
|
||||
spotLight.castShadow = true;
|
||||
threeD.scene.add(spotLight);
|
||||
//threeD.scene.add(new THREE.SpotLightHelper(spotLight));
|
||||
|
||||
threeD.controls.addEventListener("change", render);
|
||||
return true;
|
||||
}
|
||||
|
||||
// create a mesh from pixel data
|
||||
async function add3dMesh(width, height, segmentsX, segmentsY) {
|
||||
const geometry = new THREE.PlaneGeometry(width, height, segmentsX-1, segmentsY-1);
|
||||
|
||||
// generateTexture
|
||||
//threeD.material = new THREE.MeshBasicMaterial();
|
||||
//const texture = new THREE.CanvasTexture(generateTexture(grid.cells.h, grid.cellsX, grid.cellsY));
|
||||
//threeD.material.map = texture;
|
||||
|
||||
const url = await getMapURL("mesh");
|
||||
threeD.material = new THREE.MeshLambertMaterial();
|
||||
const texture = new THREE.TextureLoader().load(url, render);
|
||||
texture.needsUpdate = true;
|
||||
threeD.material.map = texture;
|
||||
|
||||
geometry.vertices.forEach((v, i) => v.z = getMeshHeight(i));
|
||||
geometry.computeVertexNormals(); // added
|
||||
threeD.Renderer.shadowMap.enabled = true; // added
|
||||
threeD.mesh = new THREE.Mesh(geometry, threeD.material);
|
||||
threeD.mesh.rotation.x = -Math.PI / 2;
|
||||
threeD.mesh.castShadow = true;
|
||||
threeD.mesh.receiveShadow = true;
|
||||
threeD.scene.add(threeD.mesh);
|
||||
}
|
||||
|
||||
function getMeshHeight(i) {
|
||||
const h = grid.cells.h[i];
|
||||
return h < 20 ? 0 : (h - 18) / 82 * threeDscale;
|
||||
}
|
||||
|
||||
function generateTexture(data, width, height) {
|
||||
let context, image, imageData;
|
||||
const vector3 = new THREE.Vector3(0, 0, 0);
|
||||
const sun = new THREE.Vector3(1, 1, 1);
|
||||
sun.normalize();
|
||||
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
context = canvas.getContext('2d');
|
||||
context.fillStyle = '#000';
|
||||
context.fillRect(0, 0, width, height);
|
||||
|
||||
image = context.getImageData(0, 0, canvas.width, canvas.height);
|
||||
imageData = image.data;
|
||||
|
||||
for (let i = 0, j = 0; i < imageData.length; i += 4, j ++) {
|
||||
vector3.x = data[j - 2] - data[j + 2];
|
||||
vector3.y = 2;
|
||||
vector3.z = data[j - width * 2] - data[j + width * 2];
|
||||
vector3.normalize();
|
||||
|
||||
const shade = vector3.dot(sun);
|
||||
// initial: r 96 + shade * 128, g 32 + shade * 96, b shade * 96;
|
||||
const clr = (shade * 255) * (.5 + data[j] * .007); // new: black and white
|
||||
imageData[i] = imageData[i + 1] = imageData[i + 2] = clr;
|
||||
}
|
||||
context.putImageData(image, 0, 0);
|
||||
|
||||
const canvasScaled = document.createElement('canvas');
|
||||
canvasScaled.width = width * 4;
|
||||
canvasScaled.height = height * 4;
|
||||
context = canvasScaled.getContext('2d');
|
||||
context.scale(4, 4);
|
||||
context.drawImage(canvas, 0, 0);
|
||||
|
||||
image = context.getImageData(0, 0, canvasScaled.width, canvasScaled.height);
|
||||
imageData = image.data;
|
||||
|
||||
for (let i = 0; i < imageData.length; i += 4) {
|
||||
const v = ~~(Math.random() * 5);
|
||||
imageData[i] += v;
|
||||
imageData[i + 1] += v;
|
||||
imageData[i + 2] += v;
|
||||
}
|
||||
context.putImageData(image, 0, 0);
|
||||
return canvasScaled;
|
||||
}
|
||||
|
||||
function loadTHREE() {
|
||||
if (window.THREE) return Promise.resolve(true);
|
||||
|
||||
return new Promise(resolve => {
|
||||
const script = document.createElement('script');
|
||||
script.src = "libs/three.min.js"
|
||||
document.head.append(script);
|
||||
script.onload = () => resolve(true);
|
||||
script.onerror = () => resolve(false);
|
||||
});
|
||||
}
|
||||
|
||||
function OrbitControls(camera, domElement) {
|
||||
if (THREE.OrbitControls) new THREE.OrbitControls(camera, domElement);
|
||||
|
||||
return new Promise(resolve => {
|
||||
const script = document.createElement('script');
|
||||
script.src = "libs/orbitControls.min.js"
|
||||
document.head.append(script);
|
||||
script.onload = () => resolve(new THREE.OrbitControls(camera, domElement));
|
||||
script.onerror = () => resolve(false);
|
||||
});
|
||||
}
|
||||
|
||||
function update3dPreview(canvas) {
|
||||
threeD.scene.remove(threeD.mesh);
|
||||
threeD.Renderer.setSize(canvas.width, canvas.height);
|
||||
if (canvas.dataset.type === "viewGlobe") addGlobe3dMesh();
|
||||
else add3dMesh(graphWidth, graphHeight, grid.cellsX, grid.cellsY);
|
||||
render();
|
||||
}
|
||||
|
||||
async function update3d() {
|
||||
const url = await getMapURL("mesh");
|
||||
threeD.material.map = new THREE.TextureLoader().load(url, render);
|
||||
}
|
||||
|
||||
function stop3d() {
|
||||
if (!threeD.controls || !threeD.Renderer) return;
|
||||
threeD.controls.dispose();
|
||||
threeD.Renderer.dispose()
|
||||
cancelAnimationFrame(threeD.animationFrame);
|
||||
threeD = {};
|
||||
}
|
||||
|
||||
async function startGlobe(canvas) {
|
||||
const loaded = await loadTHREE();
|
||||
if (!loaded) {tip("Cannot load 3d library", false, "error", 4000); return false};
|
||||
|
||||
threeD.scene = new THREE.Scene();
|
||||
threeD.scene.background = new THREE.TextureLoader().load("https://i0.wp.com/azgaar.files.wordpress.com/2019/10/stars.png", render);
|
||||
threeD.Renderer = new THREE.WebGLRenderer({canvas, antialias: true, preserveDrawingBuffer: true});
|
||||
threeD.Renderer.setSize(canvas.width, canvas.height);
|
||||
|
||||
threeD.camera = new THREE.PerspectiveCamera(45, canvas.width / canvas.height, 0.1, 1000).translateZ(5);
|
||||
threeD.controls = await OrbitControls(threeD.camera, threeD.Renderer.domElement);
|
||||
threeD.controls.minDistance = 1.8; threeD.controls.maxDistance = 10;
|
||||
threeD.controls.autoRotate = true;
|
||||
threeD.controls.keys = {};
|
||||
|
||||
const ambientLight = new THREE.AmbientLight(0xcccccc, .9);
|
||||
threeD.scene.add(ambientLight);
|
||||
const spotLight = new THREE.SpotLight(0xcccccc, .6, 200, .7, .1, 0);
|
||||
spotLight.position.set(700, 300, 200);
|
||||
spotLight.castShadow = false;
|
||||
threeD.scene.add(spotLight);
|
||||
//threeD.scene.add(new THREE.SpotLightHelper(spotLight));
|
||||
|
||||
addGlobe3dMesh();
|
||||
threeD.controls.addEventListener("change", render);
|
||||
threeD.animationFrame = requestAnimationFrame(animate);
|
||||
return true;
|
||||
}
|
||||
|
||||
// create globe mesh just from svg
|
||||
async function addGlobe3dMesh() {
|
||||
threeD.material = new THREE.MeshLambertMaterial();
|
||||
const url = await getMapURL("mesh");
|
||||
threeD.material.map = new THREE.TextureLoader().load(url, render);
|
||||
threeD.mesh = new THREE.Mesh(new THREE.SphereBufferGeometry(1, 64, 64), threeD.material);
|
||||
threeD.scene.add(threeD.mesh);
|
||||
}
|
||||
|
||||
// render 3d scene and camera, do only on controls change
|
||||
function render() {
|
||||
threeD.Renderer.render(threeD.scene, threeD.camera);
|
||||
}
|
||||
|
||||
// animate 3d scene and camera
|
||||
function animate() {
|
||||
threeD.animationFrame = requestAnimationFrame(animate);
|
||||
threeD.controls.update();
|
||||
threeD.Renderer.render(threeD.scene, threeD.camera);
|
||||
}
|
||||
|
||||
function toggleRotation() {
|
||||
const rotate = threeD.controls.autoRotate = !threeD.controls.autoRotate;
|
||||
rotate ? requestAnimationFrame(animate) : cancelAnimationFrame(threeD.animationFrame);
|
||||
}
|
||||
|
||||
// download screenshot
|
||||
async function saveScreenshot() {
|
||||
const URL = threeD.Renderer.domElement.toDataURL("image/jpeg");
|
||||
const link = document.createElement("a");
|
||||
link.download = getFileName() + ".jpeg";
|
||||
link.href = URL;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
tip(`Screenshot is saved. Open "Downloads" screen (CTRL + J) to check`, true, "success", 7000);
|
||||
window.setTimeout(() => window.URL.revokeObjectURL(URL), 5000);
|
||||
}
|
||||
|
|
@ -1,91 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
// Define variables - these make it easy to work with from the console
|
||||
let _3dpreviewScale = 70;
|
||||
let _3dpreviewCamera = null;
|
||||
let _3dpreviewScene = null;
|
||||
let _3dpreviewRenderer = null;
|
||||
let _3danimationFrame = null;
|
||||
let _3dmaterial = null;
|
||||
let _3dmesh = null;
|
||||
|
||||
// Create a mesh from pixel data
|
||||
async function addMesh(width, height, segmentsX, segmentsY) {
|
||||
const _3dgeometry = new THREE.PlaneGeometry(width, height, segmentsX-1, segmentsY-1);
|
||||
const _3dmaterial = new THREE.MeshBasicMaterial({wireframe: false});
|
||||
const url = await getMapURL("mesh");
|
||||
_3dmaterial.map = new THREE.TextureLoader().load(url);
|
||||
_3dgeometry.vertices.forEach((v, i) => v.z = getMeshHeight(i));
|
||||
_3dmesh = new THREE.Mesh(_3dgeometry, _3dmaterial);
|
||||
_3dmesh.rotation.x = -Math.PI / 2;
|
||||
_3dpreviewScene.add(_3dmesh);
|
||||
}
|
||||
|
||||
function getMeshHeight(i) {
|
||||
const h = grid.cells.h[i];
|
||||
return h < 20 ? 0 : (h - 18) / 82 * _3dpreviewScale;
|
||||
}
|
||||
|
||||
// Function to render scene and camera
|
||||
function render() {
|
||||
_3danimationFrame = requestAnimationFrame(render);
|
||||
_3dpreviewRenderer.render(_3dpreviewScene, _3dpreviewCamera);
|
||||
}
|
||||
|
||||
async function start3dpreview(canvas) {
|
||||
const loaded = await loadTHREE();
|
||||
if (!loaded) {
|
||||
tip("Cannot load 3d library", false, "error", 4000);
|
||||
return false;
|
||||
};
|
||||
_3dpreviewScene = new THREE.Scene();
|
||||
_3dpreviewCamera = new THREE.PerspectiveCamera(70, canvas.width / canvas.height, 0.1, 100000);
|
||||
_3dpreviewCamera.position.x = 0;
|
||||
_3dpreviewCamera.position.z = 350;
|
||||
_3dpreviewCamera.position.y = 285;
|
||||
_3dpreviewRenderer = new THREE.WebGLRenderer({canvas});
|
||||
OrbitControls(_3dpreviewCamera, _3dpreviewRenderer.domElement);
|
||||
_3dpreviewRenderer.setSize(canvas.width, canvas.height);
|
||||
addMesh(graphWidth, graphHeight, grid.cellsX, grid.cellsY);
|
||||
_3danimationFrame = requestAnimationFrame(render);
|
||||
return true;
|
||||
}
|
||||
|
||||
function loadTHREE() {
|
||||
if (window.THREE) return Promise.resolve(true);
|
||||
|
||||
return new Promise(resolve => {
|
||||
const script = document.createElement('script');
|
||||
script.src = "libs/three.min.js"
|
||||
document.head.append(script);
|
||||
script.onload = () => resolve(true);
|
||||
script.onerror = () => resolve(false);
|
||||
});
|
||||
}
|
||||
|
||||
function OrbitControls(camera, domElement) {
|
||||
if (THREE.OrbitControls) {
|
||||
new THREE.OrbitControls(camera, domElement);
|
||||
return;
|
||||
}
|
||||
|
||||
const script = document.createElement('script');
|
||||
script.src = "libs/orbitControls.min.js"
|
||||
document.head.append(script);
|
||||
script.onload = () => new THREE.OrbitControls(camera, domElement);
|
||||
}
|
||||
|
||||
function update3dpreview(canvas) {
|
||||
_3dpreviewScene.remove(_3dmesh);
|
||||
_3dpreviewRenderer.setSize(canvas.width, canvas.height);
|
||||
addMesh(graphWidth, graphHeight, grid.cellsX, grid.cellsY);
|
||||
}
|
||||
|
||||
function stop3dpreview() {
|
||||
cancelAnimationFrame(_3danimationFrame);
|
||||
_3danimationFrame = null;
|
||||
_3dmesh = undefined;
|
||||
_3dmaterial = undefined;
|
||||
_3dpreviewScene = null;
|
||||
_3dpreviewRenderer = null;
|
||||
}
|
||||
|
|
@ -39,6 +39,7 @@ function editDiplomacy() {
|
|||
document.getElementById("diplomacyMatrix").addEventListener("click", showRelationsMatrix);
|
||||
document.getElementById("diplomacyHistory").addEventListener("click", showRelationsHistory);
|
||||
document.getElementById("diplomacyExport").addEventListener("click", downloadDiplomacyData);
|
||||
document.getElementById("diplomacySelect").addEventListener("click", diplomacyChangeRelations);
|
||||
|
||||
function refreshDiplomacyEditor() {
|
||||
diplomacyEditorAddLines();
|
||||
|
|
@ -52,6 +53,11 @@ function editDiplomacy() {
|
|||
const sel = selectedLine ? +selectedLine.dataset.id : states.find(s => s.i && !s.removed).i;
|
||||
const selName = states[sel].fullName;
|
||||
|
||||
// move select drop-down back to initial place
|
||||
const select = document.getElementById("diplomacySelect");
|
||||
body.parentNode.insertBefore(select, body);
|
||||
select.style.display = "none";
|
||||
|
||||
let lines = `<div class="states Self" data-id=${sel}>
|
||||
<div data-tip="List below shows relations to ${selName}" style="width: 100%">${selName}</div>
|
||||
</div>`;
|
||||
|
|
@ -62,11 +68,15 @@ function editDiplomacy() {
|
|||
const index = statuses.indexOf(relation);
|
||||
const color = colors[index];
|
||||
const tip = s.fullName + description[index] + selName;
|
||||
const tipSelect = `${tip}. Click to see relations to ${s.name}`;
|
||||
const tipChange = `${tip}. Click to change relations to ${selName}`;
|
||||
|
||||
lines += `<div class="states" data-id=${s.i} data-name="${s.fullName}" data-relations="${relation}">
|
||||
<div data-tip="${tip}. Click to see relations to ${s.name}" style="width:12em">${s.fullName}</div>
|
||||
<input data-tip="${tip}. Click to see relations to ${s.name}" class="stateColor" type="color" value="${color}" disabled>
|
||||
<select data-tip="Click to change ${getAdjective(s.name)} relations to ${selName}" class="diplomacyRelations">${getRelations(relation)}</select>
|
||||
<div data-tip="${tipSelect}" style="width:12em">${s.fullName}</div>
|
||||
<svg data-tip="${tipChange}" width=".9em" height=".9em" style="margin-bottom:-1px" class="changeRelations">
|
||||
<rect x="0" y="0" width="100%" height="100%" fill="${color}" class="zoneFill" style="pointer-events: none"></rect>
|
||||
</svg>
|
||||
<input data-tip="${tipChange}" class="changeRelations diplomacyRelations" value="${relation}" readonly/>
|
||||
</div>`;
|
||||
}
|
||||
body.innerHTML = lines;
|
||||
|
|
@ -75,8 +85,7 @@ function editDiplomacy() {
|
|||
body.querySelectorAll("div.states").forEach(el => el.addEventListener("mouseenter", ev => stateHighlightOn(ev)));
|
||||
body.querySelectorAll("div.states").forEach(el => el.addEventListener("mouseleave", ev => stateHighlightOff(ev)));
|
||||
body.querySelectorAll("div.states").forEach(el => el.addEventListener("click", selectStateOnLineClick));
|
||||
body.querySelectorAll("div > select.diplomacyRelations").forEach(el => el.addEventListener("click", ev => ev.stopPropagation()));
|
||||
body.querySelectorAll("div > select.diplomacyRelations").forEach(el => el.addEventListener("change", diplomacyChangeRelations));
|
||||
body.querySelectorAll(".changeRelations").forEach(el => el.addEventListener("click", toggleDiplomacySelect));
|
||||
|
||||
applySorting(diplomacyHeader);
|
||||
$("#diplomacyEditor").dialog();
|
||||
|
|
@ -113,12 +122,6 @@ function editDiplomacy() {
|
|||
});
|
||||
}
|
||||
|
||||
function getRelations(relations) {
|
||||
let options = "";
|
||||
statuses.forEach(s => options += `<option ${relations === s ? "selected" : ""} value="${s}">${s}</option>`);
|
||||
return options;
|
||||
}
|
||||
|
||||
function showStateRelations() {
|
||||
const selectedLine = body.querySelector("div.Self");
|
||||
const sel = selectedLine ? +selectedLine.dataset.id : pack.states.find(s => s.i && !s.removed).i;
|
||||
|
|
@ -156,18 +159,33 @@ function editDiplomacy() {
|
|||
refreshDiplomacyEditor();
|
||||
}
|
||||
|
||||
function diplomacyChangeRelations() {
|
||||
function toggleDiplomacySelect(event) {
|
||||
event.stopPropagation();
|
||||
const select = document.getElementById("diplomacySelect");
|
||||
const show = select.style.display === "none";
|
||||
if (!show) {select.style.display = "none"; return;}
|
||||
event.target.parentNode.insertBefore(select, event.target);
|
||||
select.style.display = "block";
|
||||
}
|
||||
|
||||
function diplomacyChangeRelations(event) {
|
||||
event.stopPropagation();
|
||||
const select = document.getElementById("diplomacySelect");
|
||||
select.style.display = "none";
|
||||
|
||||
const subject = +event.target.parentElement.parentElement.dataset.id;
|
||||
const rel = event.target.innerHTML;
|
||||
body.parentNode.insertBefore(select, body);
|
||||
|
||||
const states = pack.states, chronicle = states[0].diplomacy;
|
||||
const selectedLine = body.querySelector("div.Self");
|
||||
const object = selectedLine ? +selectedLine.dataset.id : states.find(s => s.i && !s.removed).i;
|
||||
if (!object) return;
|
||||
const objectName = states[object].name; // object of relations change
|
||||
|
||||
const subject = +this.parentNode.dataset.id;
|
||||
const subjectName = states[subject].name; // subject of relations change - actor
|
||||
|
||||
const oldRel = states[subject].diplomacy[object];
|
||||
const rel = this.value;
|
||||
if (rel === oldRel) return;
|
||||
states[subject].diplomacy[object] = rel;
|
||||
states[object].diplomacy[subject] = rel === "Vassal" ? "Suzerain" : rel === "Suzerain" ? "Vassal" : rel;
|
||||
|
||||
|
|
|
|||
|
|
@ -279,13 +279,14 @@ document.addEventListener("keydown", event => {
|
|||
|
||||
// Hotkeys, see github.com/Azgaar/Fantasy-Map-Generator/wiki/Hotkeys
|
||||
document.addEventListener("keyup", event => {
|
||||
const active = document.activeElement.tagName;
|
||||
if (!window.closeDialogs) return; // not all modules are loaded
|
||||
const canvas3d = document.getElementById("canvas3d"); // check if 3d mode is active
|
||||
const active = canvas3d ? null : document.activeElement.tagName;
|
||||
if (active === "INPUT" || active === "SELECT" || active === "TEXTAREA") return; // don't trigger if user inputs a text
|
||||
if (active === "DIV" && document.activeElement.contentEditable === "true") return; // don't trigger if user inputs a text
|
||||
event.stopPropagation();
|
||||
|
||||
const key = event.keyCode, ctrl = event.ctrlKey, shift = event.shiftKey, meta = event.metaKey;
|
||||
const tdMode = document.getElementById("_3dpreview");
|
||||
|
||||
if (key === 112) showInfo(); // "F1" to show info
|
||||
else if (key === 113) regeneratePrompt(); // "F2" for new map
|
||||
|
|
@ -296,7 +297,12 @@ document.addEventListener("keyup", event => {
|
|||
else if (key === 27) {closeDialogs(); hideOptions();} // Escape to close all dialogs
|
||||
else if (key === 46) removeElementOnKey(); // "Delete" to remove the selected element
|
||||
|
||||
else if (key === 83 && canvas3d) saveScreenshot(); // "S" to save a screenshot
|
||||
else if (key === 82 && canvas3d) toggleRotation(); // "R" to toggle 3d rotation
|
||||
else if (key === 85 && canvas3d && customization !== 1) update3d(); // "U" to update 3d view
|
||||
|
||||
else if (ctrl && key === 80) savePNG(); // Ctrl + "P" to save as PNG
|
||||
else if (ctrl && key === 71) saveJPEG(); // Ctrl + "J" to save as JPEG
|
||||
else if (ctrl && key === 83) saveSVG(); // Ctrl + "S" to save as SVG
|
||||
else if (ctrl && key === 77) saveMap(); // Ctrl + "M" to save MAP file
|
||||
else if (ctrl && key === 71) saveGeoJSON(); // Ctrl + "G" to save as GeoJSON
|
||||
|
|
@ -357,10 +363,10 @@ document.addEventListener("keyup", event => {
|
|||
else if (key === 187) toggleRulers(); // Equal (=) to toggle Rulers
|
||||
else if (key === 189) toggleScaleBar(); // Minus (-) to toggle Scale bar
|
||||
|
||||
else if (key === 37 && !tdMode) zoom.translateBy(svg, 10, 0); // Left to scroll map left
|
||||
else if (key === 39 && !tdMode) zoom.translateBy(svg, -10, 0); // Right to scroll map right
|
||||
else if (key === 38 && !tdMode) zoom.translateBy(svg, 0, 10); // Up to scroll map up
|
||||
else if (key === 40 && !tdMode) zoom.translateBy(svg, 0, -10); // Up to scroll map up
|
||||
else if (key === 37) zoom.translateBy(svg, 10, 0); // Left to scroll map left
|
||||
else if (key === 39) zoom.translateBy(svg, -10, 0); // Right to scroll map right
|
||||
else if (key === 38) zoom.translateBy(svg, 0, 10); // Up to scroll map up
|
||||
else if (key === 40) zoom.translateBy(svg, 0, -10); // Up to scroll map up
|
||||
else if (key === 107 || key === 109) pressNumpadSign(key); // Numpad Plus/Minus to zoom map or change brush size
|
||||
else if (key === 48 || key === 96) resetZoom(1000); // 0 to reset zoom
|
||||
else if (key === 49 || key === 97) zoom.scaleTo(svg, 1); // 1 to zoom to 1
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ function editHeightmap() {
|
|||
|
||||
restartHistory();
|
||||
if (document.getElementById("preview")) document.getElementById("preview").remove();
|
||||
if (document.getElementById("_3dpreview")) toggleHeightmap3dView();
|
||||
if (document.getElementById("canvas3d")) toggleHeightmap3dView();
|
||||
|
||||
|
||||
const mode = heightmapEditMode.innerHTML;
|
||||
|
|
@ -415,7 +415,7 @@ function editHeightmap() {
|
|||
if (!noStat) {
|
||||
updateStatistics();
|
||||
if (document.getElementById("preview")) drawHeightmapPreview(); // update heightmap preview if opened
|
||||
if (document.getElementById("_3dpreview")) update3dpreview(_3dpreview); // update 3d heightmap preview if opened
|
||||
if (document.getElementById("canvas3d")) update3dPreview(canvas3d); // update 3d heightmap preview if opened
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -430,7 +430,7 @@ function editHeightmap() {
|
|||
updateStatistics();
|
||||
|
||||
if (document.getElementById("preview")) drawHeightmapPreview(); // update heightmap preview if opened
|
||||
if (document.getElementById("_3dpreview")) update3dpreview(_3dpreview); // update 3d heightmap preview if opened
|
||||
if (document.getElementById("canvas3d")) update3dPreview(canvas3d); // update 3d heightmap preview if opened
|
||||
}
|
||||
|
||||
// restart edits from 1st step
|
||||
|
|
@ -871,7 +871,7 @@ function editHeightmap() {
|
|||
updateStatistics();
|
||||
mockHeightmap();
|
||||
if (document.getElementById("preview")) drawHeightmapPreview(); // update heightmap preview if opened
|
||||
if (document.getElementById("_3dpreview")) update3dpreview(_3dpreview); // update 3d heightmap preview if opened
|
||||
if (document.getElementById("canvas3d")) update3dPreview(canvas3d); // update 3d heightmap preview if opened
|
||||
}
|
||||
|
||||
function downloadTemplate() {
|
||||
|
|
@ -1195,38 +1195,38 @@ function editHeightmap() {
|
|||
|
||||
// 3D previewer
|
||||
async function toggleHeightmap3dView() {
|
||||
if (document.getElementById("_3dpreview")) {
|
||||
$("#_3dpreviewEditor").dialog("close");
|
||||
if (document.getElementById("canvas3d")) {
|
||||
$("#preview3d").dialog("close");
|
||||
return;
|
||||
}
|
||||
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.id = "_3dpreview";
|
||||
canvas.id = "canvas3d";
|
||||
canvas.style.display = "block";
|
||||
canvas.width = parseFloat(_3dpreviewEditor.style.width) || graphWidth / 3;
|
||||
canvas.width = parseFloat(preview3d.style.width) || graphWidth / 3;
|
||||
canvas.height = canvas.width / (graphWidth / graphHeight);
|
||||
const started = await start3dpreview(canvas);
|
||||
const started = await start3d(canvas);
|
||||
if (!started) return;
|
||||
document.getElementById("_3dpreviewEditor").appendChild(canvas);
|
||||
document.getElementById("preview3d").appendChild(canvas);
|
||||
canvas.onmouseenter = () => {
|
||||
canvas.dataset.hovered ? tip("") : tip("Left mouse to change angle, middle mouse or mousewheel to zoom, right mouse to pan");
|
||||
canvas.dataset.hovered = 1;
|
||||
+canvas.dataset.hovered > 2 ? tip("") : tip("Left mouse to change angle, middle mouse or mousewheel to zoom, right mouse to pan. R to toggle rotation");
|
||||
canvas.dataset.hovered = (+canvas.dataset.hovered|0) + 1;
|
||||
};
|
||||
|
||||
$("#_3dpreviewEditor").dialog({
|
||||
$("#preview3d").dialog({
|
||||
title: "3D Preview", resizable: true,
|
||||
position: {my: "left bottom", at: "left+10 bottom-20", of: "svg"},
|
||||
resizeStop: resize3dpreview, close: close3dPreview
|
||||
resizeStop: resize3d, close: close3dPreview
|
||||
});
|
||||
|
||||
function resize3dpreview() {
|
||||
canvas.width = parseFloat(_3dpreviewEditor.style.width);
|
||||
canvas.height = parseFloat(_3dpreviewEditor.style.height) - 2;
|
||||
update3dpreview(canvas);
|
||||
function resize3d() {
|
||||
canvas.width = parseFloat(preview3d.style.width);
|
||||
canvas.height = parseFloat(preview3d.style.height) - 2;
|
||||
update3dPreview(canvas);
|
||||
}
|
||||
|
||||
function close3dPreview() {
|
||||
stop3dpreview();
|
||||
stop3d();
|
||||
canvas.remove();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,8 @@ function getDefaultPresets() {
|
|||
"religions": ["toggleBorders", "toggleIcons", "toggleLabels", "toggleReligions", "toggleRivers", "toggleRoutes", "toggleScaleBar"],
|
||||
"provinces": ["toggleBorders", "toggleIcons", "toggleProvinces", "toggleRivers", "toggleScaleBar"],
|
||||
"biomes": ["toggleBiomes", "toggleRivers", "toggleScaleBar"],
|
||||
"heightmap": ["toggleHeight", "toggleRivers", "toggleScaleBar"],
|
||||
"heightmap": ["toggleHeight", "toggleRivers"],
|
||||
"physical": ["toggleCoordinates", "toggleHeight", "toggleRivers", "toggleScaleBar"],
|
||||
"poi": ["toggleBorders", "toggleHeight", "toggleIcons", "toggleMarkers", "toggleRivers", "toggleRoutes", "toggleScaleBar"],
|
||||
"landmass": ["toggleScaleBar"]
|
||||
}
|
||||
|
|
@ -70,6 +71,7 @@ function changePreset(preset) {
|
|||
const isDefault = getDefaultPresets()[preset];
|
||||
removePresetButton.style.display = isDefault ? "none" : "inline-block";
|
||||
savePresetButton.style.display = "none";
|
||||
if (document.getElementById("canvas3d")) setTimeout(update3d, 300);
|
||||
}
|
||||
|
||||
function savePreset() {
|
||||
|
|
@ -113,6 +115,11 @@ function getCurrentPreset() {
|
|||
savePresetButton.style.display = "inline-block";
|
||||
}
|
||||
|
||||
// update 3d view is layer is toggled
|
||||
document.getElementById("mapLayers").addEventListener("click", () => {
|
||||
if (document.getElementById("canvas3d")) setTimeout(update3d, 300);
|
||||
});
|
||||
|
||||
function toggleHeight(event) {
|
||||
if (!terrs.selectAll("*").size()) {
|
||||
turnButtonOn("toggleHeight");
|
||||
|
|
|
|||
|
|
@ -348,6 +348,7 @@ document.getElementById("sticked").addEventListener("click", function(event) {
|
|||
else if (id === "saveMap") saveMap();
|
||||
else if (id === "saveSVG") saveSVG();
|
||||
else if (id === "savePNG") savePNG();
|
||||
else if (id === "saveJPEG") saveJPEG();
|
||||
else if (id === "saveGeo") saveGeoJSON();
|
||||
else if (id === "saveDropbox") saveDropbox();
|
||||
if (id === "quickSave" || id === "saveMap" || id === "saveSVG" || id === "savePNG" || id === "saveGeo" || id === "saveDropbox") toggleSavePane();
|
||||
|
|
@ -423,3 +424,46 @@ document.getElementById("mapToLoad").addEventListener("change", function() {
|
|||
closeDialogs();
|
||||
uploadMap(fileToLoad);
|
||||
});
|
||||
|
||||
// View mode
|
||||
viewMode.addEventListener("click", changeViewMode);
|
||||
function changeViewMode(event) {
|
||||
if (event.target.tagName !== "BUTTON") return;
|
||||
const button = event.target;
|
||||
|
||||
enterStandardView();
|
||||
if (button.classList.contains("pressed")) {
|
||||
button.classList.remove("pressed");
|
||||
viewStandard.classList.add("pressed");
|
||||
} else {
|
||||
viewMode.querySelectorAll(".pressed").forEach(button => button.classList.remove("pressed"));
|
||||
button.classList.add("pressed");
|
||||
if (button.id !== "viewStandard") enter3dView(button.id);
|
||||
}
|
||||
}
|
||||
|
||||
function enterStandardView() {
|
||||
if (!document.getElementById("canvas3d")) return;
|
||||
document.getElementById("canvas3d").remove();
|
||||
stop3d();
|
||||
}
|
||||
|
||||
async function enter3dView(type) {
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.id = "canvas3d";
|
||||
canvas.style.display = "block";
|
||||
canvas.width = svgWidth;
|
||||
canvas.height = svgHeight;
|
||||
canvas.style.position = "absolute";
|
||||
canvas.style.display = "none";
|
||||
canvas.dataset.type = type;
|
||||
const started = type === "viewGlobe" ? await startGlobe(canvas) : await start3d(canvas);
|
||||
if (!started) return;
|
||||
canvas.style.display = "block";
|
||||
document.body.insertBefore(canvas, optionsContainer);
|
||||
canvas.onmouseenter = () => {
|
||||
const help = "Left mouse to change angle, middle mouse / mousewheel to zoom, right mouse to pan.\r\n<b>R</b> to toggle rotation. <b>U</b> to update. <b>S</b> to get a screenshot";
|
||||
+canvas.dataset.hovered > 2 ? tip("") : tip(help);
|
||||
canvas.dataset.hovered = (+canvas.dataset.hovered|0) + 1;
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue