scene designer

This commit is contained in:
2025-03-13 18:02:48 +02:00
parent a199e59608
commit 96869a62e4
10 changed files with 483 additions and 76 deletions
+70 -70
View File
@@ -26,8 +26,8 @@
"sharp": "^0.33.5",
"three": "^0.169.0",
"uuid": "^11.0.2",
"vue": "^3.4.31",
"vuetify": "^3.6.11"
"vue": "^3.5.13",
"vuetify": "^3.7.16"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.0.5",
@@ -1544,53 +1544,53 @@
}
},
"node_modules/@vue/compiler-core": {
"version": "3.5.12",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.12.tgz",
"integrity": "sha512-ISyBTRMmMYagUxhcpyEH0hpXRd/KqDU4ymofPgl2XAkY9ZhQ+h0ovEZJIiPop13UmR/54oA2cgMDjgroRelaEw==",
"version": "3.5.13",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.13.tgz",
"integrity": "sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==",
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.25.3",
"@vue/shared": "3.5.12",
"@vue/shared": "3.5.13",
"entities": "^4.5.0",
"estree-walker": "^2.0.2",
"source-map-js": "^1.2.0"
}
},
"node_modules/@vue/compiler-dom": {
"version": "3.5.12",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.12.tgz",
"integrity": "sha512-9G6PbJ03uwxLHKQ3P42cMTi85lDRvGLB2rSGOiQqtXELat6uI4n8cNz9yjfVHRPIu+MsK6TE418Giruvgptckg==",
"version": "3.5.13",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.13.tgz",
"integrity": "sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==",
"license": "MIT",
"dependencies": {
"@vue/compiler-core": "3.5.12",
"@vue/shared": "3.5.12"
"@vue/compiler-core": "3.5.13",
"@vue/shared": "3.5.13"
}
},
"node_modules/@vue/compiler-sfc": {
"version": "3.5.12",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.12.tgz",
"integrity": "sha512-2k973OGo2JuAa5+ZlekuQJtitI5CgLMOwgl94BzMCsKZCX/xiqzJYzapl4opFogKHqwJk34vfsaKpfEhd1k5nw==",
"version": "3.5.13",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.13.tgz",
"integrity": "sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==",
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.25.3",
"@vue/compiler-core": "3.5.12",
"@vue/compiler-dom": "3.5.12",
"@vue/compiler-ssr": "3.5.12",
"@vue/shared": "3.5.12",
"@vue/compiler-core": "3.5.13",
"@vue/compiler-dom": "3.5.13",
"@vue/compiler-ssr": "3.5.13",
"@vue/shared": "3.5.13",
"estree-walker": "^2.0.2",
"magic-string": "^0.30.11",
"postcss": "^8.4.47",
"postcss": "^8.4.48",
"source-map-js": "^1.2.0"
}
},
"node_modules/@vue/compiler-ssr": {
"version": "3.5.12",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.12.tgz",
"integrity": "sha512-eLwc7v6bfGBSM7wZOGPmRavSWzNFF6+PdRhE+VFJhNCgHiF8AM7ccoqcv5kBXA2eWUfigD7byekvf/JsOfKvPA==",
"version": "3.5.13",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.13.tgz",
"integrity": "sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==",
"license": "MIT",
"dependencies": {
"@vue/compiler-dom": "3.5.12",
"@vue/shared": "3.5.12"
"@vue/compiler-dom": "3.5.13",
"@vue/shared": "3.5.13"
}
},
"node_modules/@vue/devtools-api": {
@@ -1601,53 +1601,53 @@
"license": "MIT"
},
"node_modules/@vue/reactivity": {
"version": "3.5.12",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.12.tgz",
"integrity": "sha512-UzaN3Da7xnJXdz4Okb/BGbAaomRHc3RdoWqTzlvd9+WBR5m3J39J1fGcHes7U3za0ruYn/iYy/a1euhMEHvTAg==",
"version": "3.5.13",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.13.tgz",
"integrity": "sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==",
"license": "MIT",
"dependencies": {
"@vue/shared": "3.5.12"
"@vue/shared": "3.5.13"
}
},
"node_modules/@vue/runtime-core": {
"version": "3.5.12",
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.12.tgz",
"integrity": "sha512-hrMUYV6tpocr3TL3Ad8DqxOdpDe4zuQY4HPY3X/VRh+L2myQO8MFXPAMarIOSGNu0bFAjh1yBkMPXZBqCk62Uw==",
"version": "3.5.13",
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.13.tgz",
"integrity": "sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw==",
"license": "MIT",
"dependencies": {
"@vue/reactivity": "3.5.12",
"@vue/shared": "3.5.12"
"@vue/reactivity": "3.5.13",
"@vue/shared": "3.5.13"
}
},
"node_modules/@vue/runtime-dom": {
"version": "3.5.12",
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.12.tgz",
"integrity": "sha512-q8VFxR9A2MRfBr6/55Q3umyoN7ya836FzRXajPB6/Vvuv0zOPL+qltd9rIMzG/DbRLAIlREmnLsplEF/kotXKA==",
"version": "3.5.13",
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.13.tgz",
"integrity": "sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog==",
"license": "MIT",
"dependencies": {
"@vue/reactivity": "3.5.12",
"@vue/runtime-core": "3.5.12",
"@vue/shared": "3.5.12",
"@vue/reactivity": "3.5.13",
"@vue/runtime-core": "3.5.13",
"@vue/shared": "3.5.13",
"csstype": "^3.1.3"
}
},
"node_modules/@vue/server-renderer": {
"version": "3.5.12",
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.12.tgz",
"integrity": "sha512-I3QoeDDeEPZm8yR28JtY+rk880Oqmj43hreIBVTicisFTx/Dl7JpG72g/X7YF8hnQD3IFhkky5i2bPonwrTVPg==",
"version": "3.5.13",
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.13.tgz",
"integrity": "sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA==",
"license": "MIT",
"dependencies": {
"@vue/compiler-ssr": "3.5.12",
"@vue/shared": "3.5.12"
"@vue/compiler-ssr": "3.5.13",
"@vue/shared": "3.5.13"
},
"peerDependencies": {
"vue": "3.5.12"
"vue": "3.5.13"
}
},
"node_modules/@vue/shared": {
"version": "3.5.12",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.12.tgz",
"integrity": "sha512-L2RPSAwUFbgZH20etwrXyVyCBu9OxRSi8T/38QsvnkJyvq2LufW2lDCOzm7t/U9C1mkhJGWYfCuFBCmIuNivrg==",
"version": "3.5.13",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.13.tgz",
"integrity": "sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==",
"license": "MIT"
},
"node_modules/@vuetify/loader-shared": {
@@ -5284,9 +5284,9 @@
}
},
"node_modules/nanoid": {
"version": "3.3.7",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
"version": "3.3.9",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.9.tgz",
"integrity": "sha512-SppoicMGpZvbF1l3z4x7No3OlIjP7QJvC9XR7AhZr1kL133KHnKPztkKDc+Ir4aJ/1VhTySrtKhrsycmrMQfvg==",
"funding": [
{
"type": "github",
@@ -5601,9 +5601,9 @@
"license": "MIT"
},
"node_modules/picocolors": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz",
"integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==",
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"license": "ISC"
},
"node_modules/picomatch": {
@@ -5726,9 +5726,9 @@
}
},
"node_modules/postcss": {
"version": "8.4.47",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz",
"integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==",
"version": "8.5.3",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz",
"integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==",
"funding": [
{
"type": "opencollective",
@@ -5745,8 +5745,8 @@
],
"license": "MIT",
"dependencies": {
"nanoid": "^3.3.7",
"picocolors": "^1.1.0",
"nanoid": "^3.3.8",
"picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
},
"engines": {
@@ -7278,16 +7278,16 @@
}
},
"node_modules/vue": {
"version": "3.5.12",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.12.tgz",
"integrity": "sha512-CLVZtXtn2ItBIi/zHZ0Sg1Xkb7+PU32bJJ8Bmy7ts3jxXTcbfsEfBivFYYWz1Hur+lalqGAh65Coin0r+HRUfg==",
"version": "3.5.13",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.13.tgz",
"integrity": "sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==",
"license": "MIT",
"dependencies": {
"@vue/compiler-dom": "3.5.12",
"@vue/compiler-sfc": "3.5.12",
"@vue/runtime-dom": "3.5.12",
"@vue/server-renderer": "3.5.12",
"@vue/shared": "3.5.12"
"@vue/compiler-dom": "3.5.13",
"@vue/compiler-sfc": "3.5.13",
"@vue/runtime-dom": "3.5.13",
"@vue/server-renderer": "3.5.13",
"@vue/shared": "3.5.13"
},
"peerDependencies": {
"typescript": "*"
@@ -7353,9 +7353,9 @@
}
},
"node_modules/vuetify": {
"version": "3.7.2",
"resolved": "https://registry.npmjs.org/vuetify/-/vuetify-3.7.2.tgz",
"integrity": "sha512-q0WTcRG977+a9Dqhb8TOaPm+Xmvj0oVhnBJhAdHWFSov3HhHTTxlH2nXP/GBTXZuuMHDbBeIWFuUR2/1Fx0PPw==",
"version": "3.7.16",
"resolved": "https://registry.npmjs.org/vuetify/-/vuetify-3.7.16.tgz",
"integrity": "sha512-Few/cBtgJYgdkzi0LWmVy67G5uc2+q7oWcadbcTUPAtEtGYNh2AM28h01Fk+ScJgfxkA077//ZDff1rh3jYG/w==",
"license": "MIT",
"engines": {
"node": "^12.20 || >=14.13"
+2 -2
View File
@@ -28,8 +28,8 @@
"sharp": "^0.33.5",
"three": "^0.169.0",
"uuid": "^11.0.2",
"vue": "^3.4.31",
"vuetify": "^3.6.11"
"vue": "^3.5.13",
"vuetify": "^3.7.16"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.0.5",
+2 -2
View File
@@ -8,7 +8,7 @@
</template>
<v-list>
<v-list-item to="/game-objects/add">Нов игрови обект</v-list-item>
<v-list-item>Нов сценарий</v-list-item>
<v-list-item to="/scenarios/add">Нов сценарий</v-list-item>
<v-list-item to="/games/add">Нова игра</v-list-item>
</v-list>
</v-menu>
@@ -21,7 +21,7 @@
<v-divider></v-divider>
<v-list nav>
<v-list-item prepend-icon="mdi-database" to="/game-objects/list" :title="$l.gameObjects"></v-list-item>
<v-list-item prepend-icon="mdi-receipt-text-edit-outline" :title="$l.gameScenarios"></v-list-item>
<v-list-item prepend-icon="mdi-receipt-text-edit-outline" to="/scenarios/list" :title="$l.gameScenarios"></v-list-item>
<v-list-item prepend-icon="mdi-cogs" :title="$l.gameRules"></v-list-item>
<v-divider></v-divider>
<v-list-item prepend-icon="mdi-controller" :title="$l.games" to="/games/list"></v-list-item>
@@ -0,0 +1,269 @@
<template>
<div class="container my-3">
<v-btn-toggle variant="tonal" density="compact" class="mx-auto" v-model="listMode" color="blue">
<v-btn class="text-none" value="scene"><v-icon>mdi-panorama-outline</v-icon><span>{{ $l.addScene }}</span></v-btn>
<v-btn class="text-none" value="object"><v-icon>mdi-bird</v-icon><span>{{ $l.addScene }}</span></v-btn>
<v-btn class="text-none" value="task"><v-icon>mdi-checkbox-marked-circle-plus-outline</v-icon><span>{{ $l.addScene }}</span></v-btn>
</v-btn-toggle>
<div @wheel="onWheel" @mousedown="onMouseDown" @mouseup="onMouseUp" @mousemove="onDrag"
:class="`svg-container ${mode}`" ref="svgContainer">
<svg>
</svg>
</div>
<v-navigation-drawer location="right">
<svg-scene></svg-scene>
</v-navigation-drawer>
</div>
</template>
<script>
import SvgScene from './SvgScene.vue';
const components = {
SvgScene
}
export default {
components: { SvgScene },
props:{
modelValue: Object
},
data(){
return {
listMode: 'select'
}
},
computed:{
object:()=>this.modelValue,
zoom:{
get(){
return 1 / this.scale;
},
set(v){
this.rescale(1 / v);
}
},
mode(){
return this.listMode[0];
},
components(){
return components;
},
},
methods:{
rescale(scale, e){
let oldScale = this.scale;
if (!e){
this.scale = scale;
this.offset.x += (oldScale - this.scale) * (this.$refs.svgContainer.offsetWidth/2);
this.offset.y += (oldScale - this.scale) * (this.$refs.svgContainer.offsetHeight/2);
}else{
this.scale *= (1 + Math.sign(e.deltaY) / 10);
let oo = {
x: (oldScale - this.scale) * e.offsetX,
y: (oldScale - this.scale) * e.offsetY
};
if (this.target){
this.retarget(e)
}
this.offset.x += oo.x;
this.offset.y += oo.y
}
document.documentElement.style.setProperty('--svg-scale', this.scale);
},
retarget(e){
if (this.target.delta){
this.retargetDelta(e, this.target)
}else{
this.retargetAbsolute(e, this.target)
}
},
retargetDelta(e, target){
let p = {
x: Utils.round(e.movementX*this.scale, 0),
y: Utils.round(e.movementY*this.scale, 0)
}
target.attrs.forEach(a=>{
if (Array.isArray(a)){
a[0] = p.x;
a[1] = p.y;
}else if (a.startsWith('x')){
this.target.target[a] += p.x;
}else if (a.startsWith('y')){
this.target.target[a] += p.y;
}
})
},
retargetAbsolute(e, target){
let p = {
x: Utils.round(this.vb.x + e.offsetX * this.scale),
y: Utils.round(this.vb.y + e.offsetY * this.scale)
}
target.attrs.forEach(a=>{
if (Array.isArray(a)){
a[0] = p.x;
a[1] = p.y;
}else if (a.startsWith('x')){
this.target.target[a] = p.x;
}else if (a.startsWith('y')){
this.target.target[a] = p.y;
}
})
},
onWheel(e){
this.rescale(null, e);
},
onDrag(e){
if (this.mousedown?.button == 0 || this.modeStep > 0) {
if (this.mode == 'move'){
let p = {
x: Utils.round(e.movementX*this.scale, 0),
y: Utils.round(e.movementY*this.scale, 0)
}
this.selectedItem.forEach(i=>{
let mf = components[i.name].modifiers;
mf.filter(m=>m.match(/^x[0-9]+$/)).forEach(x=>i.data[x]+= p.x);
mf.filter(m=>m.match(/^y[0-9]+$/)).forEach(y=>i.data[y]+= p.y);
})
}else if (this.target) {
this.retarget(e)
}
}
if (e.shiftKey ||
this.mousedown?.button == 1 ||
(this.mode == 'pan' && this.mousedown?.button == 0) ||
(this.mode == 'default' && this.mousedown?.button == 0 && !this.target)
){
this.offset.x -= e.movementX*this.scale;
this.offset.y -= e.movementY*this.scale;
}
//console.log(e);
},
onMouseDown(e){
this.mousedown = { button: e.button };
//console.log(e, this.mode, this.modeStep)
if (e.button == 0 && !['default', 'move', 'pan'].includes( this.mode )){
let cs;
if (this.mode == 'select'){
//console.log('selecting')
cs = [['x1', 'y1'], ['x2', 'y2']];
if (this.modeStep == 0){
this.target = {
target: this.selector,
attrs: []
}
}
}else{
cs = this.components[this.mode].steps;
//console.log(cs);
if (this.modeStep == 0){
this.target = {
target: {},
attrs: []
}
let id, nid = 1;
do {
id = `${this.components[this.mode].name}-${nid++}`
}while (this.items.find(i=>i.id == id))
this.items.push({
name: this.mode,
data: this.target.target,
visible: true,
id, title: id
})
}
}
let p = {
x: Utils.round(this.vb.x + e.offsetX * this.scale),
y: Utils.round(this.vb.y + e.offsetY * this.scale)
}
for (let i = this.modeStep + 1; i <= cs.length; i++){
this.target.target[cs[i-1][0]] = p.x;
this.target.target[cs[i-1][1]] = p.y;
}
this.modeStep++;
if (this.modeStep >= cs.length){
this.modeStep = 0;
if (this.mode == 'select'){
this.select();
this.selector.x1 = this.selector.y1 = this.selector.x2 = this.selector.y2 = 0;
}
}
if (this.modeStep ){
this.target.attrs[0] = cs[this.modeStep][0];
this.target.attrs[1] = cs[this.modeStep][1];
}
}
},
onMouseUp(){
this.mousedown = false;
if (this.mode == 'default' && !this.target){
this.selectedItem = [];
}
if (this.modeStep == 0){
this.target = null;
}
},
setTarget(t, item){
this.target = t;
this.selectedItem = [item]
},
async save(){
let imageData = await Utils.blobToBase64(await (await fetch(this.imageTarget)).blob());
//console.log(imageData)
let jsonData = {
items: this.items,
viewBox: this.viewBox,
offset: this.offset,
zoom: this.zoom,
rotation: this.imgRotation,
page: this.page,
data: imageData
};
const link = document.createElement('a')
const blob = new Blob([JSON.stringify(jsonData)], {type: 'application/json'});
link.href = URL.createObjectURL(blob)
link.download = "document.json"
link.click();
},
select(){
let r = Utils.adjustMinMax(this.selector);
this.selectedItem = this.items.filter(i=>this.$refs['svg-'+i.id][0].intersect(r));
},
async processImage(){
this.processingImage = true;
await this.$nextTick();
let processor = new ImageProcessor(this.$refs.imageCanvas, this.img)
let lines = processor.identifyLines(), iw = this.img.naturalWidth;
lines.forEach((l, i)=>{
this.items.push({
name: 'SvgHorizontalLine', id: `aline-${i}`, visible: true, title: `ALine-${i}`,
data: { x1:iw*.1, y1: l, x2: iw*.9}
})
})
this.processingImage = false;
}
}
}
</script>
<style lang="scss">
.svg-container{
svg{
width: 100%;
min-height: 100vh;
}
image{
clip-path: circle(50% at 50% 50%);
}
circle {
stroke: rgb(var(--v-theme-primary));
fill:rgba(255,255,255,.5);
stroke-width: 2px;
}
}
</style>
@@ -0,0 +1,28 @@
<template>
<circle :cx="65 + x" :cy="65 + y" :r="70"></circle>
<image :href="src" :x="x" :y="y" height="130" width="130" preserveAspectRatio="xMidYMid slice"></image>
</template>
<script>
export default {
props:['src', 'x', 'y'],
data(){
return {
target: null,
img: null,
size:{}
}
},
created(){
// this.img = new Image();
// this.img.onload = ()=>{
// this.size = {
// w: this.img.naturalWidth,
// h: this.img.naturalHeight,
// a: this.img.naturalWidth / this.img.naturalHeight
// }
// }
// this.img.src= this.target;
}
}
</script>
+25
View File
@@ -0,0 +1,25 @@
<template>
<teleport to="svg" defer>
<g>
<svg-avatar src="/asset/thumb/6.webp" :x="50" :y="50"></svg-avatar>
</g>
</teleport>
<v-list density="compact" nav>
<v-list-item prepend-icon="mdi-panorama-outline" :title="$l.addScene" value="scene"></v-list-item>
</v-list>
</template>
<script>
import SvgAvatar from './SvgAvatar.vue';
export default {
components: { SvgAvatar },
data(){
return {
active: false
}
},
mounted(){
this.active = true;
}
}
</script>
+7
View File
@@ -35,6 +35,7 @@ class GameEngine {
// renderer.shadowMap.type = THREE.PCFSoftShadowMap; // default THREE.PCFShadowMap
renderer.outputEncoding = THREE.sRGBEncoding;
const controls = new OrbitControls( camera, renderer.domElement );
//controls.enableZoom = true;
//const controls = new MapControls( camera, renderer.domElement );
this.transformControls = new TransformControls( camera, renderer.domElement );
this.transformControls.addEventListener( 'dragging-changed', function ( event ) {
@@ -64,6 +65,12 @@ class GameEngine {
scene.background = bck; //new THREE.Color(0.7,0.7,0.7);
scene.environment = texture;
console.log('GameEngine started')
renderer.domElement.addEventListener('wheel', (event)=>{
camera.zoom -= event.deltaY / 1000;
camera.zoom = Math.max(camera.zoom, .4);
controls.rotateSpeed = 1 / camera.zoom;
camera.updateProjectionMatrix();
})
}
$ = THREE;
+43
View File
@@ -0,0 +1,43 @@
<template>
<v-card :title="id == 'add' ? $l.createScenario : $l.editScenario" class="container my-3">
<v-form class="pa-4" v-model="valid">
<v-text-field :label="$l.name" v-model="object.name" :rules="[rules.required]"></v-text-field>
</v-form>
<!-- <v-card-actions>
<v-btn @click="saveAndPreview" :loading="loading" prepend-icon="mdi-content-save" color="primary"
:disabled="!valid">
{{ $l.saveAndPreview }}
</v-btn>
<v-btn @click="publish" prepend-icon="mdi-publish" color="success" v-if="false && object.id">{{ $l.publish
}}</v-btn>
</v-card-actions> -->
</v-card>
<SceneDesigner v-model="object"></SceneDesigner>
<div class="sceneDrawer" >
</div>
</template>
<script>
import SceneDesigner from '@/components/SceneDesigner/SceneDesigner.vue';
export default {
data() {
return {
object: {},
valid: false,
rules: {
required: v => v ? true : this.$l.fieldRequired,
requiredFile: v => (v?.length || this.id != 'add') ? true : this.$l.fieldRequired
},
loading: false,
}
},
computed: {
id() {
return this.$route.params?.id;
}
},
}
</script>
+9
View File
@@ -0,0 +1,9 @@
<template>
</template>
<script>
export default {
}
</script>
+28 -2
View File
@@ -23,10 +23,36 @@ const lang = {
darkMode: 'Тъмен режим',
confirmDeletionOf: 'Потвърдете изтриването на',
yes: 'Да',
no: 'Не'
no: 'Не',
createScenario: 'Създаване на сценарий',
editScenario: 'Редкатиране на сценарий',
addScene: 'Добавяне на сцена'
},
en: {
createGameObject: 'Add game object',
editGameObject: 'Edit game object',
name: 'Name',
fieldRequired: 'Field is required',
objectType: 'Object type',
objectFile: 'File',
panorama2d: 'Panorama picture',
environment3d: 'Environment',
object3d: '3D object',
object2d: '2D object (picture)',
audio: 'Audio',
player3d: 'Player',
saveAndPreview: 'Save and preview',
preview: 'Preview',
captureThumbnail: 'Save thumbnail',
publish: 'Publish',
gameObjects: 'Objects',
gameScenarios: 'Scenarios',
gameRules: 'Rules',
games: 'Games',
darkMode: 'Dark mode',
confirmDeletionOf: 'Confirm deletion of',
yes: 'Yes',
no: 'No'
}
}