mirror of
https://github.com/LinoSchmidt/StickExporterTX.git
synced 2026-03-20 17:44:29 +01:00
added webpack dev server, slightly improved ui and render preview
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -264,4 +264,7 @@ out/
|
||||
output/
|
||||
|
||||
blender-win/
|
||||
blender-linux/
|
||||
blender-linux/
|
||||
blender/
|
||||
|
||||
index.build.js
|
||||
@@ -1,16 +1,15 @@
|
||||
from ast import Str
|
||||
import csv
|
||||
from importlib.resources import path
|
||||
import logging
|
||||
import math
|
||||
import sys
|
||||
import time
|
||||
import bpy
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
argv = sys.argv
|
||||
argv = argv[argv.index("--") + 1:]
|
||||
|
||||
settings = ET.parse(argv[0])
|
||||
|
||||
logger = logging.getLogger('simple_example')
|
||||
logger.setLevel(logging.INFO)
|
||||
formatter = logging.Formatter('%(message)s')
|
||||
@@ -19,20 +18,16 @@ console_handler.setFormatter(formatter)
|
||||
console_handler.setLevel(logging.INFO)
|
||||
logger.addHandler(console_handler)
|
||||
|
||||
def _map(x, in_min, in_max, out_min, out_max):
|
||||
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
|
||||
GimbalL = bpy.data.objects["GimbalL"]
|
||||
StickL = bpy.data.objects["StickL"]
|
||||
GimbalR = bpy.data.objects["GimbalR"]
|
||||
StickR = bpy.data.objects["StickR"]
|
||||
GimbalCoverR = bpy.data.objects["GimbalCoverR"]
|
||||
TrailR = bpy.data.objects["TrailR"]
|
||||
Camera = bpy.data.objects["Camera"]
|
||||
Plane = bpy.data.objects["Plane.001"]
|
||||
scn = bpy.context.scene
|
||||
|
||||
settingsRoot = settings.getroot()
|
||||
|
||||
FPS = int(settingsRoot[0].text)
|
||||
Width = int(settingsRoot[1].text)
|
||||
StickDistance = _map(int(settingsRoot[2].text), 0, 100, 5, 105)
|
||||
StickMode = settingsRoot[3].text
|
||||
if(StickMode == "true"):
|
||||
StickMode = 2
|
||||
else:
|
||||
StickMode = 1
|
||||
|
||||
lyMax = 0.436
|
||||
lyMin = -0.436
|
||||
lxMax = -0.436
|
||||
@@ -41,128 +36,175 @@ ryMax = -0.436
|
||||
ryMin = 0.436
|
||||
rxMax = 0.436
|
||||
rxMin = -0.436
|
||||
|
||||
logs = settingsRoot[4].text[1:][:-1].split("\"\"")
|
||||
|
||||
logCount = len(logs)
|
||||
logNumber = 1
|
||||
def _map(x, in_min, in_max, out_min, out_max):
|
||||
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
|
||||
|
||||
for log in logs:
|
||||
logger.info("Lognr:" + ((str)(logNumber)) + ":")
|
||||
logger.info("Blender started successfully!")
|
||||
|
||||
while True:
|
||||
command = input("Waiting for command: ")
|
||||
|
||||
logTime = []
|
||||
rud = []
|
||||
ele = []
|
||||
thr = []
|
||||
ail = []
|
||||
time.sleep(0.5)
|
||||
|
||||
try:
|
||||
with open(log, newline='') as csvFile:
|
||||
reader = csv.DictReader(csvFile)
|
||||
for row in reader:
|
||||
logTime.append(row['Time'].split(":").pop(2).replace(".", ""))
|
||||
rud.append(int(row['Rud']))
|
||||
ele.append(int(row['Ele']))
|
||||
thr.append(int(row['Thr']))
|
||||
ail.append(int(row['Ail']))
|
||||
settingsRoot = ET.parse(argv[0]+"/settings.xml").getroot()
|
||||
|
||||
StickMode = settingsRoot[3].text
|
||||
if(StickMode == "true"):
|
||||
StickMode = 2
|
||||
else:
|
||||
StickMode = 1
|
||||
|
||||
fps = int(settingsRoot[0].text)
|
||||
width = int(settingsRoot[1].text)
|
||||
StickDistance = _map(int(settingsRoot[2].text), 0, 100, 5, 105)
|
||||
logs = settingsRoot[4].text[1:][:-1].split("\"\"")
|
||||
output = settingsRoot[5].text
|
||||
|
||||
if(command == "startRendering"):
|
||||
|
||||
meanTime = []
|
||||
i = 0
|
||||
while i < len(logTime)-1:
|
||||
if int(logTime[i]) > int(logTime[i+1]):
|
||||
meanTime.append(60000 - int(logTime[i]) + int(logTime[i+1]))
|
||||
else:
|
||||
meanTime.append(int(logTime[i+1]) - int(logTime[i]))
|
||||
i+=1
|
||||
logCount = len(logs)
|
||||
logNumber = 1
|
||||
|
||||
totalTime = 0
|
||||
for e in meanTime:
|
||||
totalTime+=e
|
||||
|
||||
frameCount = math.floor(totalTime/1000*FPS-1)
|
||||
FPSxxx = 1000/FPS
|
||||
except Exception as e:
|
||||
print("Can't read Log!")
|
||||
exit()
|
||||
|
||||
GimbalL = bpy.data.objects["GimbalL"]
|
||||
StickL = bpy.data.objects["StickL"]
|
||||
GimbalR = bpy.data.objects["GimbalR"]
|
||||
StickR = bpy.data.objects["StickR"]
|
||||
GimbalCoverR = bpy.data.objects["GimbalCoverR"]
|
||||
TrailR = bpy.data.objects["TrailR"]
|
||||
Camera = bpy.data.objects["Camera"]
|
||||
Plane = bpy.data.objects["Plane.001"]
|
||||
scn = bpy.context.scene
|
||||
|
||||
scn.render.resolution_x = Width
|
||||
GimbalCoverR.location[0] = StickDistance
|
||||
GimbalR.location[0] = StickDistance
|
||||
TrailR.location[0] = StickDistance
|
||||
Plane.location[0] = StickDistance
|
||||
Camera.location[0] = StickDistance/2
|
||||
Camera.data.ortho_scale = StickDistance+5
|
||||
scn.render.resolution_y = int(Width/_map(StickDistance, 5, 105, 2, 21.6))
|
||||
bpy.context.scene.render.filepath = settingsRoot[5].text + "\\" + log.split("/")[-1].split("\\")[-1].replace(".csv", ".mov")
|
||||
|
||||
scn.render.fps = 1000
|
||||
scn.render.fps_base = FPSxxx
|
||||
|
||||
scn.frame_start = 0
|
||||
scn.frame_end = frameCount+1
|
||||
logger.info("Frames:" + str(frameCount+1) + ":")
|
||||
|
||||
frame = 0
|
||||
log = 0
|
||||
pastTime = 0
|
||||
while frame <= frameCount:
|
||||
currentTime = math.floor(FPSxxx*frame)
|
||||
while currentTime >= pastTime+meanTime[log]:
|
||||
pastTime+=meanTime[log]
|
||||
log+=1
|
||||
for log in logs:
|
||||
logger.info("Lognr:" + ((str)(logNumber)) + ":")
|
||||
|
||||
multiplier = (currentTime-pastTime)/meanTime[log]
|
||||
|
||||
ailP = _map(ail[log]+(ail[log+1]-ail[log])*multiplier, -1024, 1024, rxMin, rxMax)
|
||||
eleP = _map(ele[log]+(ele[log+1]-ele[log])*multiplier, -1024, 1024, ryMin, ryMax)
|
||||
rudP = _map(rud[log]+(rud[log+1]-rud[log])*multiplier, -1024, 1024, lyMin, lyMax)
|
||||
thrP = _map(thr[log]+(thr[log+1]-thr[log])*multiplier, -1024, 1024, lxMin, lxMax)
|
||||
|
||||
bpy.context.scene.frame_set(frame)
|
||||
|
||||
if StickMode == "1":
|
||||
StickL.rotation_euler=[0,0,0]
|
||||
StickL.rotation_euler.rotate_axis("Y", ailP)
|
||||
StickL.keyframe_insert(data_path="rotation_euler", index=-1)
|
||||
GimbalL.rotation_euler=[0,0,0]
|
||||
GimbalL.rotation_euler.rotate_axis("X", eleP)
|
||||
GimbalL.keyframe_insert(data_path="rotation_euler", index=-1)
|
||||
StickR.rotation_euler=[0,0,0]
|
||||
StickR.rotation_euler.rotate_axis("Y", rudP)
|
||||
StickR.keyframe_insert(data_path="rotation_euler", index=-1)
|
||||
GimbalR.rotation_euler=[0,0,0]
|
||||
GimbalR.rotation_euler.rotate_axis("X", thrP)
|
||||
GimbalR.keyframe_insert(data_path="rotation_euler", index=-1)
|
||||
else:
|
||||
StickL.rotation_euler=[0,0,0]
|
||||
StickL.rotation_euler.rotate_axis("Y", rudP)
|
||||
StickL.keyframe_insert(data_path="rotation_euler", index=-1)
|
||||
GimbalL.rotation_euler=[0,0,0]
|
||||
GimbalL.rotation_euler.rotate_axis("X", thrP)
|
||||
GimbalL.keyframe_insert(data_path="rotation_euler", index=-1)
|
||||
StickR.rotation_euler=[0,0,0]
|
||||
StickR.rotation_euler.rotate_axis("Y", ailP)
|
||||
StickR.keyframe_insert(data_path="rotation_euler", index=-1)
|
||||
GimbalR.rotation_euler=[0,0,0]
|
||||
GimbalR.rotation_euler.rotate_axis("X", eleP)
|
||||
GimbalR.keyframe_insert(data_path="rotation_euler", index=-1)
|
||||
|
||||
logger.info("Init:" + ((str)(frame)) + ":")
|
||||
frame+=1
|
||||
logTime = []
|
||||
rud = []
|
||||
ele = []
|
||||
thr = []
|
||||
ail = []
|
||||
|
||||
try:
|
||||
with open(log, newline='') as csvFile:
|
||||
reader = csv.DictReader(csvFile)
|
||||
for row in reader:
|
||||
logTime.append(row['Time'].split(":").pop(2).replace(".", ""))
|
||||
rud.append(int(row['Rud']))
|
||||
ele.append(int(row['Ele']))
|
||||
thr.append(int(row['Thr']))
|
||||
ail.append(int(row['Ail']))
|
||||
|
||||
meanTime = []
|
||||
i = 0
|
||||
while i < len(logTime)-1:
|
||||
if int(logTime[i]) > int(logTime[i+1]):
|
||||
meanTime.append(60000 - int(logTime[i]) + int(logTime[i+1]))
|
||||
else:
|
||||
meanTime.append(int(logTime[i+1]) - int(logTime[i]))
|
||||
i+=1
|
||||
|
||||
totalTime = 0
|
||||
for e in meanTime:
|
||||
totalTime+=e
|
||||
|
||||
frameCount = math.floor(totalTime/1000*fps-1)
|
||||
FPSxxx = 1000/fps
|
||||
except Exception as e:
|
||||
logger.error("Can't read Log: " + (Str)(e))
|
||||
|
||||
bpy.context.scene.render.image_settings.file_format = 'FFMPEG'
|
||||
bpy.context.scene.render.ffmpeg.format = "QUICKTIME"
|
||||
bpy.context.scene.render.ffmpeg.codec = "QTRLE"
|
||||
|
||||
scn.render.resolution_x = width
|
||||
GimbalCoverR.location[0] = StickDistance
|
||||
GimbalR.location[0] = StickDistance
|
||||
TrailR.location[0] = StickDistance
|
||||
Plane.location[0] = StickDistance
|
||||
Camera.location[0] = StickDistance/2
|
||||
Camera.data.ortho_scale = StickDistance+5
|
||||
scn.render.resolution_y = int(width/_map(StickDistance, 5, 105, 2, 21.6))
|
||||
bpy.context.scene.render.filepath = output + "\\" + log.split("/")[-1].split("\\")[-1].replace(".csv", ".mov")
|
||||
|
||||
scn.render.fps = 1000
|
||||
scn.render.fps_base = FPSxxx
|
||||
|
||||
scn.frame_start = 0
|
||||
scn.frame_end = frameCount
|
||||
logger.info("Frames:" + str(frameCount) + ":")
|
||||
|
||||
frame = 0
|
||||
log = 0
|
||||
pastTime = 0
|
||||
while frame <= frameCount:
|
||||
currentTime = math.floor(FPSxxx*frame)
|
||||
while currentTime >= pastTime+meanTime[log]:
|
||||
pastTime+=meanTime[log]
|
||||
log+=1
|
||||
|
||||
multiplier = (currentTime-pastTime)/meanTime[log]
|
||||
|
||||
ailP = _map(ail[log]+(ail[log+1]-ail[log])*multiplier, -1024, 1024, rxMin, rxMax)
|
||||
eleP = _map(ele[log]+(ele[log+1]-ele[log])*multiplier, -1024, 1024, ryMin, ryMax)
|
||||
rudP = _map(rud[log]+(rud[log+1]-rud[log])*multiplier, -1024, 1024, lyMin, lyMax)
|
||||
thrP = _map(thr[log]+(thr[log+1]-thr[log])*multiplier, -1024, 1024, lxMin, lxMax)
|
||||
|
||||
bpy.context.scene.frame_set(frame)
|
||||
|
||||
if StickMode == "1":
|
||||
StickL.rotation_euler=[0,0,0]
|
||||
StickL.rotation_euler.rotate_axis("Y", ailP)
|
||||
StickL.keyframe_insert(data_path="rotation_euler", index=-1)
|
||||
GimbalL.rotation_euler=[0,0,0]
|
||||
GimbalL.rotation_euler.rotate_axis("X", eleP)
|
||||
GimbalL.keyframe_insert(data_path="rotation_euler", index=-1)
|
||||
StickR.rotation_euler=[0,0,0]
|
||||
StickR.rotation_euler.rotate_axis("Y", rudP)
|
||||
StickR.keyframe_insert(data_path="rotation_euler", index=-1)
|
||||
GimbalR.rotation_euler=[0,0,0]
|
||||
GimbalR.rotation_euler.rotate_axis("X", thrP)
|
||||
GimbalR.keyframe_insert(data_path="rotation_euler", index=-1)
|
||||
else:
|
||||
StickL.rotation_euler=[0,0,0]
|
||||
StickL.rotation_euler.rotate_axis("Y", rudP)
|
||||
StickL.keyframe_insert(data_path="rotation_euler", index=-1)
|
||||
GimbalL.rotation_euler=[0,0,0]
|
||||
GimbalL.rotation_euler.rotate_axis("X", thrP)
|
||||
GimbalL.keyframe_insert(data_path="rotation_euler", index=-1)
|
||||
StickR.rotation_euler=[0,0,0]
|
||||
StickR.rotation_euler.rotate_axis("Y", ailP)
|
||||
StickR.keyframe_insert(data_path="rotation_euler", index=-1)
|
||||
GimbalR.rotation_euler=[0,0,0]
|
||||
GimbalR.rotation_euler.rotate_axis("X", eleP)
|
||||
GimbalR.keyframe_insert(data_path="rotation_euler", index=-1)
|
||||
|
||||
logger.info("Init:" + ((str)(frame)) + ":")
|
||||
frame+=1
|
||||
|
||||
bpy.ops.render.render(animation=True)
|
||||
|
||||
if(logCount <= logNumber):
|
||||
logger.info("Finished")
|
||||
|
||||
logNumber+=1
|
||||
|
||||
bpy.ops.render.render(animation=True)
|
||||
|
||||
if(logCount <= logNumber):
|
||||
logger.info("Finished")
|
||||
|
||||
logNumber+=1
|
||||
elif(command == "getRender"):
|
||||
|
||||
bpy.context.scene.render.image_settings.file_format = 'PNG'
|
||||
bpy.context.scene.frame_set(0)
|
||||
bpy.context.scene.render.filepath = argv[0] + "\\render.png"
|
||||
|
||||
scn.render.resolution_x = width
|
||||
GimbalCoverR.location[0] = StickDistance
|
||||
GimbalR.location[0] = StickDistance
|
||||
TrailR.location[0] = StickDistance
|
||||
Plane.location[0] = StickDistance
|
||||
Camera.location[0] = StickDistance/2
|
||||
Camera.data.ortho_scale = StickDistance+5
|
||||
scn.render.resolution_y = int(width/_map(StickDistance, 5, 105, 2, 21.6))
|
||||
|
||||
StickL.rotation_euler=[0,0,0]
|
||||
StickL.rotation_euler.rotate_axis("Y", 0.436)
|
||||
StickL.keyframe_insert(data_path="rotation_euler", index=-1)
|
||||
GimbalL.rotation_euler=[0,0,0]
|
||||
GimbalL.rotation_euler.rotate_axis("X", 0.236)
|
||||
GimbalL.keyframe_insert(data_path="rotation_euler", index=-1)
|
||||
StickR.rotation_euler=[0,0,0]
|
||||
StickR.rotation_euler.rotate_axis("Y", -0.056)
|
||||
StickR.keyframe_insert(data_path="rotation_euler", index=-1)
|
||||
GimbalR.rotation_euler=[0,0,0]
|
||||
GimbalR.rotation_euler.rotate_axis("X", -0.436)
|
||||
GimbalR.keyframe_insert(data_path="rotation_euler", index=-1)
|
||||
|
||||
bpy.ops.render.render(write_still=True)
|
||||
@@ -3,7 +3,6 @@ import ESLintPlugin from "eslint-webpack-plugin";
|
||||
import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin';
|
||||
|
||||
export default {
|
||||
mode: 'production',
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
|
||||
11
configs/webpack.main.dev.config.babel.js
Normal file
11
configs/webpack.main.dev.config.babel.js
Normal file
@@ -0,0 +1,11 @@
|
||||
import baseConfig from "./webpack.base.config.js";
|
||||
import {merge} from 'webpack-merge';
|
||||
|
||||
export default merge(baseConfig, {
|
||||
mode: 'development',
|
||||
target: 'electron-main',
|
||||
entry: './src/index.ts',
|
||||
output: {
|
||||
filename: '../index.build.js'
|
||||
},
|
||||
});
|
||||
@@ -2,9 +2,10 @@ import baseConfig from "./webpack.base.config.js";
|
||||
import {merge} from 'webpack-merge';
|
||||
|
||||
export default merge(baseConfig, {
|
||||
mode: 'production',
|
||||
target: 'electron-main',
|
||||
entry: './src/index.ts',
|
||||
output: {
|
||||
filename: 'index.js'
|
||||
filename: '../index.build.js'
|
||||
},
|
||||
});
|
||||
31
configs/webpack.renderer.dev.config.babel.js
Normal file
31
configs/webpack.renderer.dev.config.babel.js
Normal file
@@ -0,0 +1,31 @@
|
||||
import baseConfig from "./webpack.base.config.js";
|
||||
import {merge} from 'webpack-merge';
|
||||
import { spawn } from 'child_process';
|
||||
|
||||
const host = process.env.HOST || 'localhost';
|
||||
const port = process.env.PORT || 3000;
|
||||
|
||||
export default merge(baseConfig, {
|
||||
mode: 'development',
|
||||
target: 'electron-renderer',
|
||||
entry: './src/renderer.tsx',
|
||||
output: {
|
||||
filename: 'renderer.js',
|
||||
},
|
||||
devServer: {
|
||||
compress: true,
|
||||
hot: true,
|
||||
host,
|
||||
port,
|
||||
onBeforeSetupMiddleware() {
|
||||
console.log('Starting Main Process...');
|
||||
spawn('npm', ['run', 'start:main'], {
|
||||
shell: true,
|
||||
env: process.env,
|
||||
stdio: 'inherit',
|
||||
})
|
||||
.on('close', (code) => process.exit(code))
|
||||
.on('error', (spawnError) => console.error(spawnError));
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -2,6 +2,7 @@ import baseConfig from "./webpack.base.config.js";
|
||||
import {merge} from 'webpack-merge';
|
||||
|
||||
export default merge(baseConfig, {
|
||||
mode: 'production',
|
||||
target: 'electron-renderer',
|
||||
entry: './src/renderer.tsx',
|
||||
output: {
|
||||
706
package-lock.json
generated
706
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
53
package.json
53
package.json
@@ -3,13 +3,15 @@
|
||||
"name": "stickexportertx",
|
||||
"productName": "StickExporterTX",
|
||||
"description": "3D stick exporter for EdgeTX/OpenTX logs",
|
||||
"main": "src/.webpack/index.js",
|
||||
"main": "src/index.build.js",
|
||||
"scripts": {
|
||||
"build:main": "webpack --config configs/webpack.main.config.babel.js",
|
||||
"build:renderer": "webpack --config configs/webpack.renderer.config.babel.js",
|
||||
"build": "npm run build:main && npm run build:renderer",
|
||||
"start": "npm run build && electron .",
|
||||
"build:app": "npm run build && electron-builder build"
|
||||
"build:main": "cross-env NODE_ENV=production webpack --config configs/webpack.main.prod.config.babel.js",
|
||||
"build:renderer": "cross-env NODE_ENV=production webpack --config configs/webpack.renderer.prod.config.babel.js",
|
||||
"build": "cross-env npm run build:main && cross-env npm run build:renderer",
|
||||
"bundle": "cross-env npm run build && electron-builder build",
|
||||
"start:renderer": "cross-env NODE_ENV=development webpack serve --config configs/webpack.renderer.dev.config.babel.js",
|
||||
"start:main": "cross-env NODE_ENV=development webpack --config configs/webpack.main.dev.config.babel.js && cross-env NODE_ENV=development electron .",
|
||||
"start": "cross-env npm run start:renderer"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": {
|
||||
@@ -18,33 +20,6 @@
|
||||
"url": "https://link.lino3d.de"
|
||||
},
|
||||
"license": "MIT",
|
||||
"config": {
|
||||
"forge": {
|
||||
"packagerConfig": {},
|
||||
"makers": [
|
||||
{
|
||||
"name": "@electron-forge/maker-squirrel",
|
||||
"config": {
|
||||
"name": "stickexportertx"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "@electron-forge/maker-zip",
|
||||
"platforms": [
|
||||
"darwin"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "@electron-forge/maker-deb",
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "@electron-forge/maker-rpm",
|
||||
"config": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"build": {
|
||||
"appId": "de.lino3d.stickexportertx",
|
||||
"productName": "StickExporterTX",
|
||||
@@ -54,7 +29,8 @@
|
||||
"package.json"
|
||||
],
|
||||
"extraFiles": [
|
||||
"assets/template.blend"
|
||||
"assets/template.blend",
|
||||
"assets/blenderScript.py"
|
||||
],
|
||||
"win": {
|
||||
"icon": "icon.png",
|
||||
@@ -63,7 +39,7 @@
|
||||
],
|
||||
"extraFiles": [
|
||||
{
|
||||
"from": "assets/blender-win/",
|
||||
"from": "assets/blender/",
|
||||
"to": "assets/blender/",
|
||||
"filter": [
|
||||
"**/*"
|
||||
@@ -86,7 +62,7 @@
|
||||
],
|
||||
"extraFiles": [
|
||||
{
|
||||
"from": "assets/blender-linux/",
|
||||
"from": "assets/blender/",
|
||||
"to": "assets/blender/",
|
||||
"filter": [
|
||||
"**/*"
|
||||
@@ -102,7 +78,7 @@
|
||||
],
|
||||
"extraFiles": [
|
||||
{
|
||||
"from": "assets/blender-mac/",
|
||||
"from": "assets/blender/",
|
||||
"to": "assets/blender/",
|
||||
"filter": [
|
||||
"**/*"
|
||||
@@ -124,6 +100,7 @@
|
||||
"@electron/remote": "^2.0.8",
|
||||
"electron-log": "^4.4.7",
|
||||
"electron-squirrel-startup": "^1.0.0",
|
||||
"electron-updater": "^5.0.1",
|
||||
"react": "^18.1.0",
|
||||
"react-dom": "^18.1.0",
|
||||
"xml-formatter": "^2.6.1"
|
||||
@@ -144,6 +121,7 @@
|
||||
"@typescript-eslint/parser": "^5.27.0",
|
||||
"@vercel/webpack-asset-relocator-loader": "^1.7.2",
|
||||
"babel-loader": "^8.2.5",
|
||||
"cross-env": "^7.0.3",
|
||||
"css-loader": "^6.7.1",
|
||||
"electron": "18.1.0",
|
||||
"electron-builder": "^23.0.3",
|
||||
@@ -153,7 +131,6 @@
|
||||
"eslint-plugin-react-hooks": "^4.5.0",
|
||||
"eslint-webpack-plugin": "^3.1.1",
|
||||
"fork-ts-checker-webpack-plugin": "^7.2.11",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"node-loader": "^2.0.0",
|
||||
"sass": "^1.52.1",
|
||||
"sass-loader": "^13.0.0",
|
||||
|
||||
145
src/components/blender-controller.ts
Normal file
145
src/components/blender-controller.ts
Normal file
@@ -0,0 +1,145 @@
|
||||
import { blenderPath, blenderScriptPath, dataPath, templatePath } from "./paths";
|
||||
import {spawn} from "child_process";
|
||||
import logger from "./logger";
|
||||
import { setBlenderLoading, setBlenderStatus } from "./ui/menu";
|
||||
import { setLogNumber, setStatus } from "./ui/mainSide";
|
||||
import {imageLoading, imageLoaded} from "./ui/settingsSide";
|
||||
|
||||
const blenderStartString = [
|
||||
templatePath,
|
||||
"--background",
|
||||
"--python",
|
||||
blenderScriptPath,
|
||||
"--",
|
||||
dataPath.replaceAll("\\", "/")
|
||||
]
|
||||
|
||||
let blenderConsole = spawn(blenderPath, blenderStartString);
|
||||
let readyToAcceptCommand = false;
|
||||
let renderingPicture = false;
|
||||
let renderingVideo = false;
|
||||
let waitingForRender = false;
|
||||
|
||||
function startBlender() {
|
||||
let frames = "0";
|
||||
let lastFrame = "0";
|
||||
|
||||
blenderConsole.stdout.on('data', function(data) {
|
||||
const dataStr = data.toString();
|
||||
|
||||
logger.info("Blender: " + dataStr);
|
||||
|
||||
if (dataStr.includes("Blender started successfully")) {
|
||||
renderingPicture = false;
|
||||
renderingVideo = false;
|
||||
setBlenderStatus("Started");
|
||||
}
|
||||
if (dataStr.includes("Blender quit")) {
|
||||
if(renderingPicture) {
|
||||
logger.errorMSG("Rendering preview Failed!");
|
||||
} else if(renderingVideo) {
|
||||
logger.errorMSG("Rendering video Failed!");
|
||||
}
|
||||
|
||||
readyToAcceptCommand = false;
|
||||
renderingPicture = false;
|
||||
renderingVideo = false;
|
||||
setBlenderStatus("Restarting");
|
||||
setBlenderLoading(true);
|
||||
restartBlender();
|
||||
}
|
||||
|
||||
if(dataStr.includes("Frames:")) {
|
||||
frames = dataStr.split(":")[1];
|
||||
renderingVideo = true;
|
||||
readyToAcceptCommand = false;
|
||||
setBlenderStatus("Rendering");
|
||||
setBlenderLoading(true);
|
||||
}
|
||||
if(dataStr.includes("Fra:") && renderingVideo) {
|
||||
lastFrame = dataStr.split(":")[1].split(" ")[0];
|
||||
setStatus("Rendering Frame " + lastFrame + "/" + frames);
|
||||
}
|
||||
if(dataStr.includes("Finished") && renderingVideo) {
|
||||
if(lastFrame == frames) {
|
||||
setStatus("Finished Render Successfully!");
|
||||
} else {
|
||||
logger.errorMSG("Render Failed!");
|
||||
}
|
||||
}
|
||||
if(dataStr.includes("Init:") && renderingVideo) {
|
||||
setStatus("Initialize Frame " + dataStr.split(":")[1] + "/" + frames);
|
||||
}
|
||||
if(dataStr.includes("Lognr:") && renderingVideo) {
|
||||
setLogNumber(dataStr.split(":")[1]);
|
||||
}
|
||||
|
||||
if(dataStr.includes("Waiting for command")) {
|
||||
if(renderingPicture) {
|
||||
imageLoaded();
|
||||
}
|
||||
|
||||
if(!waitingForRender) {
|
||||
readyToAcceptCommand = true;
|
||||
renderingPicture = false;
|
||||
renderingVideo = false;
|
||||
setBlenderStatus("Ready");
|
||||
setBlenderLoading(false);
|
||||
} else {
|
||||
waitingForRender = false;
|
||||
renderingPicture = true;
|
||||
blenderConsole.stdin.write("getRender\n");
|
||||
setBlenderStatus("Rendering");
|
||||
setBlenderLoading(true);
|
||||
imageLoading();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
blenderConsole.stderr.on('data', function(data:string) {
|
||||
logger.errorMSG("Blender: " + data);
|
||||
});
|
||||
}
|
||||
|
||||
function restartBlender() {
|
||||
blenderConsole.kill();
|
||||
blenderConsole = spawn(blenderPath, blenderStartString);
|
||||
startBlender();
|
||||
}
|
||||
|
||||
enum blenderCmd {
|
||||
getRender,
|
||||
startRendering,
|
||||
stopRendering,
|
||||
}
|
||||
|
||||
function blender(command:blenderCmd) {
|
||||
if(command === blenderCmd.getRender) {
|
||||
if(readyToAcceptCommand) {
|
||||
readyToAcceptCommand = false;
|
||||
renderingPicture = true;
|
||||
imageLoading();
|
||||
blenderConsole.stdin.write("getRender\n");
|
||||
} else {
|
||||
waitingForRender = true;
|
||||
}
|
||||
} else if(command === blenderCmd.startRendering) {
|
||||
if(readyToAcceptCommand) {
|
||||
readyToAcceptCommand = false;
|
||||
renderingVideo = true;
|
||||
blenderConsole.stdin.write("startRendering\n");
|
||||
}
|
||||
} else if(command === blenderCmd.stopRendering) {
|
||||
restartBlender();
|
||||
readyToAcceptCommand = false;
|
||||
renderingPicture = false;
|
||||
renderingVideo = false;
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
blender,
|
||||
blenderCmd,
|
||||
startBlender,
|
||||
renderingPicture
|
||||
}
|
||||
@@ -4,6 +4,6 @@ import {app} from '@electron/remote';
|
||||
export const dataPath = app.getPath('userData');
|
||||
export const SettingPath = path.join(dataPath, "settings.xml");
|
||||
|
||||
export const blenderPath = path.join("assets", "blender-win", "blender");
|
||||
export const blenderPath = path.join("assets", "blender", "blender");
|
||||
export const templatePath = path.join("assets", "template.blend");
|
||||
export const blenderScriptPath = path.join("assets", "blenderScript.py");
|
||||
@@ -1,43 +0,0 @@
|
||||
import {blenderPath, blenderScriptPath, SettingPath, templatePath} from "./paths";
|
||||
import {exec} from "child_process";
|
||||
import logger from "./logger";
|
||||
import React from "react";
|
||||
|
||||
function Render(setStatusDisplay:React.Dispatch<React.SetStateAction<string>>, setLogNumber:React.Dispatch<React.SetStateAction<string>>) {
|
||||
const blenderCons = exec('"' + blenderPath + '" "' + templatePath + '" --background --python "' + blenderScriptPath + '" -- "' + SettingPath.replaceAll('\\', '/') + '"', {maxBuffer: Infinity});
|
||||
|
||||
let frames = "0";
|
||||
let lastFrame = "0";
|
||||
let lastData = "Initializing...";
|
||||
|
||||
setStatusDisplay(lastData);
|
||||
|
||||
blenderCons.stdout?.on("data", (data:string) => {
|
||||
logger.info(data);
|
||||
|
||||
if(data.startsWith("Frames:")) {
|
||||
frames = data.split(":")[1];
|
||||
} else if(data.startsWith("Fra:")) {
|
||||
lastFrame = data.split(":")[1].split(" ")[0]
|
||||
lastData = "Render Frame " + lastFrame + "/" + frames;
|
||||
} else if(data.startsWith("Finished")) {
|
||||
if(lastFrame == frames) {
|
||||
lastData = "Finished Render Successfully!"
|
||||
} else {
|
||||
logger.errorMSG("Render Failed!");
|
||||
}
|
||||
} else if(data.includes("Blender quit")) {
|
||||
if(lastData != "Finished Render Successfully!") {
|
||||
logger.errorMSG("Render Failed!");
|
||||
}
|
||||
} else if(data.startsWith("Init:")) {
|
||||
lastData = "Initialize Frame " + data.split(":")[1] + "/" + frames;
|
||||
} else if(data.startsWith("Lognr:")) {
|
||||
setLogNumber(data.split(":")[1]);
|
||||
}
|
||||
|
||||
setStatusDisplay(lastData);
|
||||
});
|
||||
}
|
||||
|
||||
export default Render;
|
||||
@@ -3,11 +3,16 @@ import { dialog } from "@electron/remote";
|
||||
import { settingList, updateSettings } from "../settings";
|
||||
import logger from "../logger";
|
||||
import {exec} from "child_process";
|
||||
import Render from "../render";
|
||||
import {blender, blenderCmd} from "../blender-controller";
|
||||
|
||||
let setStatus:React.Dispatch<React.SetStateAction<string>>;
|
||||
let setLogNumber:React.Dispatch<React.SetStateAction<string>>;
|
||||
|
||||
function MainSide() {
|
||||
const [status, setStatus] = useState("Idle");
|
||||
const [logNumber, setLogNumber] = useState("0");
|
||||
const [status, setStatusInner] = useState("Idle");
|
||||
setStatus = setStatusInner;
|
||||
const [logNumber, setLogNumberInner] = useState("0");
|
||||
setLogNumber = setLogNumberInner;
|
||||
const [logs, setLogs] = useState(settingList.log);
|
||||
const [output, setOutput] = useState(settingList.output);
|
||||
const [logTable, setLogTable] = useState(logs.substring(1).slice(0, -1).split('""').map((log, index) => {
|
||||
@@ -25,7 +30,12 @@ function MainSide() {
|
||||
|
||||
return (
|
||||
<div id="content">
|
||||
<button onClick={() => Render(setStatus, setLogNumber)}>Start Render</button>
|
||||
<button id="start-render" onClick={() => blender(blenderCmd.startRendering)}>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512">
|
||||
{/* <!--! Font Awesome Pro 6.1.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --> */}
|
||||
<path d="M361 215C375.3 223.8 384 239.3 384 256C384 272.7 375.3 288.2 361 296.1L73.03 472.1C58.21 482 39.66 482.4 24.52 473.9C9.377 465.4 0 449.4 0 432V80C0 62.64 9.377 46.63 24.52 38.13C39.66 29.64 58.21 29.99 73.03 39.04L361 215z"/>
|
||||
</svg>
|
||||
</button>
|
||||
<p>{"Log " + logNumber + "/" + String(settingList.log.split("\"\"").length)}</p>
|
||||
<div className="dataDiv">
|
||||
<p>{status}</p>
|
||||
@@ -95,4 +105,8 @@ function openOutputFolder() {
|
||||
}
|
||||
}
|
||||
|
||||
export default MainSide;
|
||||
export default MainSide;
|
||||
export {
|
||||
setStatus,
|
||||
setLogNumber
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from "react";
|
||||
import React, {useState} from "react";
|
||||
import { openSide, Side } from "../../renderer";
|
||||
|
||||
const UpdateButton = () => (
|
||||
@@ -25,12 +25,46 @@ const OtherSideButtons = () => (
|
||||
<button id="settings-back" onClick={() => openSide(Side.Main)}>Back</button>
|
||||
)
|
||||
|
||||
const Menu = ({updateAvailable, side}:{updateAvailable:boolean, side:Side}) => (
|
||||
<header>
|
||||
<h1 id="main-headline">{(side == Side.Main)? "StickExporterTX" : "Settings"}</h1>
|
||||
{updateAvailable? <UpdateButton/> : null}
|
||||
{(side == Side.Main)? <MainSideButtons/> : <OtherSideButtons/>}
|
||||
</header>
|
||||
const BlenderLoadingSVG = () => (
|
||||
<svg id="blender-loading-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
||||
{/* Font Awesome Pro 6.1.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. */}
|
||||
<path d="M304 48C304 74.51 282.5 96 256 96C229.5 96 208 74.51 208 48C208 21.49 229.5 0 256 0C282.5 0 304 21.49 304 48zM304 464C304 490.5 282.5 512 256 512C229.5 512 208 490.5 208 464C208 437.5 229.5 416 256 416C282.5 416 304 437.5 304 464zM0 256C0 229.5 21.49 208 48 208C74.51 208 96 229.5 96 256C96 282.5 74.51 304 48 304C21.49 304 0 282.5 0 256zM512 256C512 282.5 490.5 304 464 304C437.5 304 416 282.5 416 256C416 229.5 437.5 208 464 208C490.5 208 512 229.5 512 256zM74.98 437C56.23 418.3 56.23 387.9 74.98 369.1C93.73 350.4 124.1 350.4 142.9 369.1C161.6 387.9 161.6 418.3 142.9 437C124.1 455.8 93.73 455.8 74.98 437V437zM142.9 142.9C124.1 161.6 93.73 161.6 74.98 142.9C56.24 124.1 56.24 93.73 74.98 74.98C93.73 56.23 124.1 56.23 142.9 74.98C161.6 93.73 161.6 124.1 142.9 142.9zM369.1 369.1C387.9 350.4 418.3 350.4 437 369.1C455.8 387.9 455.8 418.3 437 437C418.3 455.8 387.9 455.8 369.1 437C350.4 418.3 350.4 387.9 369.1 369.1V369.1z"/>
|
||||
</svg>
|
||||
)
|
||||
const BlenderReadySVG = () => (
|
||||
<svg id="blender-ready-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
|
||||
{/* Font Awesome Pro 6.1.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. */}
|
||||
<path d="M438.6 105.4C451.1 117.9 451.1 138.1 438.6 150.6L182.6 406.6C170.1 419.1 149.9 419.1 137.4 406.6L9.372 278.6C-3.124 266.1-3.124 245.9 9.372 233.4C21.87 220.9 42.13 220.9 54.63 233.4L159.1 338.7L393.4 105.4C405.9 92.88 426.1 92.88 438.6 105.4H438.6z"/>
|
||||
</svg>
|
||||
)
|
||||
|
||||
export default Menu;
|
||||
let setBlenderLoading:React.Dispatch<React.SetStateAction<boolean>>;
|
||||
let setBlenderStatus:React.Dispatch<React.SetStateAction<string>>;
|
||||
|
||||
function Menu({updateAvailable, side}:{updateAvailable:boolean, side:Side}) {
|
||||
|
||||
const [blenderLoading, setBlenderLoadingInner] = useState(true);
|
||||
setBlenderLoading = setBlenderLoadingInner;
|
||||
const [blenderStatus, setBlenderStatusInner] = useState("Starting");
|
||||
setBlenderStatus = setBlenderStatusInner;
|
||||
|
||||
return (
|
||||
<header>
|
||||
<h1 id="main-headline">{(side == Side.Main)? "StickExporterTX" : "Settings"}</h1>
|
||||
<div id="blender-info">
|
||||
<div id="blender-icon">
|
||||
{blenderLoading? <BlenderLoadingSVG/> : <BlenderReadySVG/>}
|
||||
</div>
|
||||
<p>{blenderStatus}</p>
|
||||
</div>
|
||||
{updateAvailable? <UpdateButton/> : null}
|
||||
{(side == Side.Main)? <MainSideButtons/> : <OtherSideButtons/>}
|
||||
</header>
|
||||
)
|
||||
}
|
||||
|
||||
export default Menu;
|
||||
export {
|
||||
setBlenderLoading,
|
||||
setBlenderStatus
|
||||
}
|
||||
@@ -1,5 +1,25 @@
|
||||
import React, {useState} from "react";
|
||||
import React, {useState, useEffect} from "react";
|
||||
import { settingList, updateSettings, settingListLoadDefault } from "../settings";
|
||||
import {blender, blenderCmd, renderingPicture} from "../blender-controller";
|
||||
import {dataPath} from "../paths";
|
||||
import path from "path";
|
||||
|
||||
let setRenderImg:React.Dispatch<React.SetStateAction<string>>;
|
||||
let setRenderLoading:React.Dispatch<React.SetStateAction<boolean>>;
|
||||
let sideLoaded = false;
|
||||
|
||||
function picturePath() {
|
||||
return path.join(dataPath, "render.png?t="+Date.now());
|
||||
}
|
||||
|
||||
const RenderLoadingSpinner = () => (
|
||||
<div id="renderLoadingDiv">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
||||
{/* Font Awesome Pro 6.1.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. */}
|
||||
<path d="M304 48C304 74.51 282.5 96 256 96C229.5 96 208 74.51 208 48C208 21.49 229.5 0 256 0C282.5 0 304 21.49 304 48zM304 464C304 490.5 282.5 512 256 512C229.5 512 208 490.5 208 464C208 437.5 229.5 416 256 416C282.5 416 304 437.5 304 464zM0 256C0 229.5 21.49 208 48 208C74.51 208 96 229.5 96 256C96 282.5 74.51 304 48 304C21.49 304 0 282.5 0 256zM512 256C512 282.5 490.5 304 464 304C437.5 304 416 282.5 416 256C416 229.5 437.5 208 464 208C490.5 208 512 229.5 512 256zM74.98 437C56.23 418.3 56.23 387.9 74.98 369.1C93.73 350.4 124.1 350.4 142.9 369.1C161.6 387.9 161.6 418.3 142.9 437C124.1 455.8 93.73 455.8 74.98 437V437zM142.9 142.9C124.1 161.6 93.73 161.6 74.98 142.9C56.24 124.1 56.24 93.73 74.98 74.98C93.73 56.23 124.1 56.23 142.9 74.98C161.6 93.73 161.6 124.1 142.9 142.9zM369.1 369.1C387.9 350.4 418.3 350.4 437 369.1C455.8 387.9 455.8 418.3 437 437C418.3 455.8 387.9 455.8 369.1 437C350.4 418.3 350.4 387.9 369.1 369.1V369.1z"/>
|
||||
</svg>
|
||||
</div>
|
||||
);
|
||||
|
||||
function SettingsSide() {
|
||||
|
||||
@@ -7,36 +27,47 @@ function SettingsSide() {
|
||||
const [width, setWidth] = useState(settingList.width);
|
||||
const [stickDistance, setStickDistance] = useState(settingList.stickDistance);
|
||||
const [stickMode2, setStickMode2] = useState(settingList.stickMode2);
|
||||
const [renderImg, setRenderImgInner] = useState(picturePath());
|
||||
setRenderImg = setRenderImgInner;
|
||||
const [renderLoading, setRenderLoadingInner] = useState(renderingPicture);
|
||||
setRenderLoading = setRenderLoadingInner;
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => {
|
||||
updateSettings({fps, width, stickDistance, stickMode2});
|
||||
blender(blenderCmd.getRender);
|
||||
}, 500);
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}, [fps, width, stickDistance, stickMode2]);
|
||||
|
||||
sideLoaded = true;
|
||||
|
||||
return (
|
||||
<div id="content">
|
||||
<div className="dataDiv">
|
||||
<p>FPS: </p>
|
||||
<input id="fpsInput" type="number" value={fps.toString()} min="1" step="1" onChange={e => {
|
||||
updateSettings({fps:parseInt(e.target.value)});
|
||||
setFps(settingList.fps);
|
||||
<input id="fpsInput" type="number" defaultValue={fps.toString()} min="1" step="1" onChange={e => {
|
||||
if(e.target.value.trim().length !== 0) setFps(parseInt(e.target.value));
|
||||
}}/>
|
||||
</div>
|
||||
<div className="dataDiv">
|
||||
<p>Width: </p>
|
||||
<input id="widthInput" type="number" value={width.toString()} min="1" step="1" onChange={e => {
|
||||
updateSettings({width:parseInt(e.target.value)});
|
||||
setWidth(settingList.width);
|
||||
<input id="widthInput" type="number" defaultValue={width.toString()} min="1" step="1" onChange={e => {
|
||||
if(e.target.value.trim().length !== 0) setWidth(parseInt(e.target.value));
|
||||
}}/>
|
||||
</div>
|
||||
<div className="dataDiv">
|
||||
<p>Stick Distance: </p>
|
||||
<input id="stickDistanceInput" type="number" value={stickDistance.toString()} min="0" step="1" onChange={e => {
|
||||
updateSettings({stickDistance:parseInt(e.target.value)});
|
||||
setStickDistance(settingList.stickDistance);
|
||||
<input id="stickDistanceInput" type="number" defaultValue={stickDistance.toString()} min="0" step="1" onChange={e => {
|
||||
if(e.target.value.trim().length !== 0) setStickDistance(parseInt(e.target.value));
|
||||
}}/>
|
||||
</div>
|
||||
<div className="dataDiv">
|
||||
<p>Stick Mode:</p>
|
||||
<label htmlFor="stickMode" className="toggle-switchy" data-style="rounded" data-text="12">
|
||||
<input checked={stickMode2} type="checkbox" id="stickMode" onChange={e => {
|
||||
updateSettings({stickMode2:e.target.checked});
|
||||
setStickMode2(settingList.stickMode2);
|
||||
<input defaultChecked={stickMode2} type="checkbox" id="stickMode" onChange={e => {
|
||||
setStickMode2(e.target.checked);
|
||||
}}/>
|
||||
<span className="toggle">
|
||||
<span className="switch"></span>
|
||||
@@ -51,8 +82,26 @@ function SettingsSide() {
|
||||
setStickDistance(settingList.stickDistance);
|
||||
setStickMode2(settingList.stickMode2);
|
||||
}}>Reset Settings</button>
|
||||
<div id="renderImgDiv">
|
||||
<img id="render-ex" src={renderImg}></img>
|
||||
{renderLoading? <RenderLoadingSpinner/> : null}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default SettingsSide;
|
||||
function imageLoading() {
|
||||
if(sideLoaded) setRenderLoading(true);
|
||||
}
|
||||
function imageLoaded() {
|
||||
if(sideLoaded) {
|
||||
setRenderImg(picturePath());
|
||||
setRenderLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
export default SettingsSide;
|
||||
export {
|
||||
imageLoading,
|
||||
imageLoaded
|
||||
};
|
||||
@@ -36,6 +36,8 @@ header {
|
||||
}
|
||||
header h1 {
|
||||
margin-right: auto;
|
||||
display: flex;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
#settings-back {
|
||||
@@ -90,7 +92,6 @@ header h1 {
|
||||
border: none;
|
||||
outline: none;
|
||||
margin-right: 15px;
|
||||
display: none;
|
||||
}
|
||||
#update-available svg {
|
||||
width: 35px;
|
||||
@@ -101,4 +102,91 @@ header h1 {
|
||||
}
|
||||
#update-available:hover {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
#blender-info {
|
||||
margin-right: auto;
|
||||
}
|
||||
#blender-info p {
|
||||
margin-top: 5px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
#blender-icon {
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
align-items: center;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
#blender-icon svg {
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
fill: white;
|
||||
}
|
||||
#blender-loading-icon {
|
||||
animation: rotate-icon 1.2s linear infinite;
|
||||
}
|
||||
#blender-loading-icon path {
|
||||
fill: #2196F3;
|
||||
}
|
||||
#blender-ready-icon path {
|
||||
fill: #00c24a;
|
||||
}
|
||||
|
||||
#renderLoadingDiv {
|
||||
position: absolute;
|
||||
background-color: rgba(0,0,0,0.9);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 10px;
|
||||
}
|
||||
#renderLoadingDiv svg {
|
||||
height: 50%;
|
||||
fill: #2196F3;
|
||||
animation: rotate-icon 1.2s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes rotate-icon {
|
||||
to {
|
||||
transform: rotate(1turn);
|
||||
}
|
||||
}
|
||||
|
||||
#start-render {
|
||||
cursor: pointer;
|
||||
background-color: #00c24a;
|
||||
border-radius: 50%;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
align-items: center;
|
||||
border: none;
|
||||
outline: none;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
#start-render svg {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
fill: white;
|
||||
margin-left: 5px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
#start-render:hover {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
#render-ex {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
border-radius: 10px;
|
||||
}
|
||||
#renderImgDiv {
|
||||
position: relative;
|
||||
margin-top: 15px;
|
||||
width: 100%;
|
||||
}
|
||||
@@ -7,6 +7,13 @@
|
||||
<body style="background-color: #172336;margin:0;padding:0;color:white;font-family:sans-serif;">
|
||||
<div id="root"></div>
|
||||
|
||||
<script src="./.webpack/renderer.js"></script>
|
||||
<script>
|
||||
if(process.env.NODE_ENV === 'development') {
|
||||
const port = process.env.PORT || 3000;
|
||||
document.write('<script src="http://localhost:'+port+'/renderer.js"><\/script>');
|
||||
} else {
|
||||
document.write('<script src="./.webpack/renderer.js"><\/script>');
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
14
src/index.ts
14
src/index.ts
@@ -1,5 +1,6 @@
|
||||
import {app, BrowserWindow} from 'electron';
|
||||
import {initialize as remoteInitialize, enable as remoteEnable} from '@electron/remote/main';
|
||||
import path from 'path';
|
||||
|
||||
// Handle creating/removing shortcuts on Windows when installing/uninstalling.
|
||||
if (require('electron-squirrel-startup')) {
|
||||
@@ -14,20 +15,21 @@ const createWindow = () => {
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
contextIsolation: false
|
||||
contextIsolation: false,
|
||||
}
|
||||
});
|
||||
|
||||
if(app.isPackaged) mainWindow.setMenu(null);
|
||||
// remove the menu bar when in production.
|
||||
if(process.env.NODE_ENV === 'production') mainWindow.setMenu(null);
|
||||
|
||||
remoteInitialize();
|
||||
remoteEnable(mainWindow.webContents);
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile("src/index.html");
|
||||
// load the index.html of the app.
|
||||
mainWindow.loadFile(path.join(__dirname, 'index.html'));
|
||||
|
||||
// Open the DevTools.
|
||||
if(!app.isPackaged) mainWindow.webContents.openDevTools();
|
||||
// Open the DevTools when in development mode.
|
||||
if(process.env.NODE_ENV === 'development') mainWindow.webContents.openDevTools();
|
||||
};
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
|
||||
@@ -1,30 +1,32 @@
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import Menu from "./components/ui/menu";
|
||||
import MainSide from "./components/ui/mainSide";
|
||||
import SettingsSite from "./components/ui/settingsSide";
|
||||
import "./index.css";
|
||||
import "./toggle-switchy.css";
|
||||
import { startBlender } from "./components/blender-controller";
|
||||
|
||||
enum Side {
|
||||
Main,
|
||||
Settings
|
||||
}
|
||||
|
||||
const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement);
|
||||
function openSide(side:Side) {
|
||||
ReactDOM.render(
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<Menu updateAvailable={true} side={side}/>
|
||||
<Menu updateAvailable={false} side={side}/>
|
||||
{(side == Side.Main)? <MainSide/> : <SettingsSite/>}
|
||||
</React.StrictMode>,
|
||||
document.getElementById('root'));
|
||||
</React.StrictMode>
|
||||
);
|
||||
}
|
||||
|
||||
openSide(Side.Main);
|
||||
|
||||
|
||||
|
||||
startBlender();
|
||||
|
||||
export {
|
||||
openSide,
|
||||
Side,
|
||||
Side
|
||||
}
|
||||
Reference in New Issue
Block a user