Merge pull request #13 from AlekPet/model_select_list
3d Model select list
This commit is contained in:
+86
-4
@@ -56,7 +56,10 @@ export class ThreeCanvas {
|
||||
this.VIEWS3 = views3
|
||||
|
||||
// Fix camers
|
||||
this.fixCamers = fixCamers;
|
||||
this.fixCamers = fixCamers;
|
||||
|
||||
// List models
|
||||
this.listModels = []
|
||||
}
|
||||
|
||||
getDom(idx=0) {
|
||||
@@ -111,7 +114,54 @@ export class ThreeCanvas {
|
||||
return wrapperCanvas
|
||||
}
|
||||
|
||||
createPanel(){
|
||||
// get models function
|
||||
async getModelRequest (pWrapper) {
|
||||
// Create options elements
|
||||
const addOptionsEl = (result) => {
|
||||
const threeCanvasList = pWrapper.querySelector(".threeCanvasListModels")
|
||||
const models_options = []
|
||||
|
||||
models_options.push($el("option", {textContent: "Select model", disabled: true, selected: true}))
|
||||
threeCanvasList.innerHTML = ""
|
||||
|
||||
for(let type in result.models){
|
||||
models_options.push($el("option", {textContent: `[${type[0].toUpperCase()+type.slice(1)}]`, disabled: true}))
|
||||
|
||||
|
||||
models_options.push(...result.models[type].map((m)=> {
|
||||
let path = m.path
|
||||
const [subfolder, name] = path.split("/")
|
||||
|
||||
if(path.indexOf("ThreeViewModels/") === -1){
|
||||
path = new URL(path, import.meta.url).href
|
||||
} else {
|
||||
path = `/view?filename=${name}&type=input&subfolder=${subfolder}`
|
||||
}
|
||||
|
||||
this.listModels.push(name)
|
||||
return $el("option", {value: path, textContent: m.name, dataset: {path: m.path}})
|
||||
}))
|
||||
}
|
||||
threeCanvasList.append(...models_options)
|
||||
}
|
||||
|
||||
// Get models
|
||||
return fetch("/lth/models", { cache: "no-store" }).then((resp)=> resp?.json()).then((result)=>{
|
||||
if(result?.models){
|
||||
addOptionsEl(result)
|
||||
}
|
||||
}).catch((err)=>{
|
||||
console.log("Error loading list, loading default list!")
|
||||
fetch("./models_default.json", { cache: "no-store" }).then((resp)=>resp.json()).then((result)=>{
|
||||
if(result){
|
||||
addOptionsEl(result)
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
createPanel(){
|
||||
// Add panel widget
|
||||
const panelWrapper = $el("div.threeCanvasPanelWrapper", {}, [
|
||||
$el(
|
||||
@@ -127,6 +177,32 @@ export class ThreeCanvas {
|
||||
textContent: "X",
|
||||
onclick: (e) => this.clear(true),
|
||||
}),
|
||||
$el("select.threeCanvasListModels", {
|
||||
onchange: (e) => {
|
||||
const target = e.target
|
||||
this.loaderGltf.load(target.value, ( glb ) => {
|
||||
this.addModel( glb );
|
||||
|
||||
// Save currentModel path
|
||||
this.savedData.currentModel = target.options[target.selectedIndex].dataset?.path
|
||||
panelWrapper.parentElement.dispatchEvent( new CustomEvent("threeview_model_added", { bubbles: true, detail: {currentModel: this.savedData.currentModel}}) )
|
||||
|
||||
},
|
||||
( data ) => {/* console.log( `Loaded data: ${data.loaded}/${data.total}` */},
|
||||
( err ) => {
|
||||
console.log( err );
|
||||
this.addHeadTest();
|
||||
})
|
||||
},
|
||||
}),
|
||||
$el("button.threeCanvasListModelsRefresh", {
|
||||
textContent: "↺",
|
||||
onclick: (e)=>{
|
||||
e.target.style.disabled = true
|
||||
this.getModelRequest(panelWrapper).then(()=> {e.target.style.disabled = false})
|
||||
|
||||
}
|
||||
}),
|
||||
$el("button.threeCanvasButton.threeCanvasSize", {
|
||||
textContent: "Canvas size",
|
||||
onclick: (e) => {
|
||||
@@ -173,7 +249,9 @@ export class ThreeCanvas {
|
||||
})]),
|
||||
]
|
||||
),
|
||||
]);
|
||||
]);
|
||||
|
||||
this.getModelRequest(panelWrapper)
|
||||
|
||||
return panelWrapper
|
||||
}
|
||||
@@ -445,6 +523,11 @@ export class ThreeCanvas {
|
||||
|
||||
this.directGlb( content, fname, params );
|
||||
|
||||
if(this.listModels.includes(fname)){
|
||||
console.log(`Model name ${fname} already exist in the list loaded models!`)
|
||||
return;
|
||||
}
|
||||
|
||||
// Load file to server
|
||||
const body = new FormData();
|
||||
body.append("image", new Blob([content]), fname);
|
||||
@@ -580,7 +663,6 @@ export class ThreeCanvas {
|
||||
|
||||
// update camera and render
|
||||
this.setCamera( pos, center, near, far );
|
||||
|
||||
}
|
||||
|
||||
/*addObjectToScene(type, parameters = {}) {
|
||||
|
||||
+17
-13
@@ -1,11 +1,15 @@
|
||||
.threeWrapper {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 5px;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.threeWrapper3Views{
|
||||
grid-template-columns: 1fr 1fr;
|
||||
.threeCanvasListModels {
|
||||
max-width: 100px;
|
||||
}
|
||||
|
||||
.threeWrapper3Views {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
|
||||
.threeCanvasPanelWrapper {
|
||||
@@ -41,7 +45,7 @@
|
||||
}
|
||||
|
||||
.wrapperThreeViewCanvas {
|
||||
position: relative;
|
||||
position: relative;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
display: block;
|
||||
@@ -49,7 +53,7 @@
|
||||
border-width: 1px;
|
||||
}
|
||||
|
||||
.lil-gui {
|
||||
.lil-gui {
|
||||
--padding: 2px;
|
||||
--spacing: 2px;
|
||||
--widget-height: 14px;
|
||||
@@ -58,14 +62,14 @@
|
||||
--widget-border-radius: 0px;
|
||||
--slider-input-min-width: 30px;
|
||||
--color-input-min-width: 30px;
|
||||
--width:150px;
|
||||
--name-width:30%;
|
||||
--background-color:none;
|
||||
--title-background-color:none;
|
||||
--width: 150px;
|
||||
--name-width: 30%;
|
||||
--background-color: none;
|
||||
--title-background-color: none;
|
||||
--text-color: #ebebeb;
|
||||
--title-text-color: #ebebeb;
|
||||
--widget-color: rgba(20,20,30,0.6);
|
||||
--hover-color: rgba(30,30,50,0.8);
|
||||
--widget-color: rgba(20, 20, 30, 0.6);
|
||||
--hover-color: rgba(30, 30, 50, 0.8);
|
||||
--focus-color: #595959;
|
||||
--number-color: #2cc9ff;
|
||||
--string-color: #a2db3c;
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"models": {
|
||||
"default": [
|
||||
{
|
||||
"name": "column.glb",
|
||||
"size": 35128,
|
||||
"path": "./assets/column.glb"
|
||||
},
|
||||
{
|
||||
"name": "dog.glb",
|
||||
"size": 37312,
|
||||
"path": "./assets/dog.glb"
|
||||
},
|
||||
{
|
||||
"name": "eva00.glb",
|
||||
"size": 450112,
|
||||
"path": "./assets/eva00.glb"
|
||||
},
|
||||
{
|
||||
"name": "eva01.glb",
|
||||
"size": 446184,
|
||||
"path": "./assets/eva01.glb"
|
||||
},
|
||||
{
|
||||
"name": "eva02.glb",
|
||||
"size": 450816,
|
||||
"path": "./assets/eva02.glb"
|
||||
},
|
||||
{
|
||||
"name": "head.glb",
|
||||
"size": 2346688,
|
||||
"path": "./assets/head.glb"
|
||||
},
|
||||
{
|
||||
"name": "head2.glb",
|
||||
"size": 125672,
|
||||
"path": "./assets/head2.glb"
|
||||
},
|
||||
{
|
||||
"name": "lee.glb",
|
||||
"size": 107424,
|
||||
"path": "./assets/lee.glb"
|
||||
},
|
||||
{
|
||||
"name": "man.glb",
|
||||
"size": 462112,
|
||||
"path": "./assets/man.glb"
|
||||
},
|
||||
{
|
||||
"name": "woman.glb",
|
||||
"size": 465272,
|
||||
"path": "./assets/woman.glb"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
+18
-7
@@ -61,7 +61,7 @@ async function widgetThreeJS(node, nodeData, inputData, app, params = {}) {
|
||||
widget = node.addDOMWidget("threeCanvas", "custom_widget", threeCanvas.threeWrapper, {
|
||||
getValue() {
|
||||
return threeCanvas.getSavedOptions();
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const origDraw = widget.draw;
|
||||
@@ -85,8 +85,13 @@ async function widgetThreeJS(node, nodeData, inputData, app, params = {}) {
|
||||
widget.threeCanvas = threeCanvas;
|
||||
widget.threeWrapper = threeCanvas.threeWrapper;
|
||||
|
||||
document.body.addEventListener("threeview_model_added", ({detail})=>{
|
||||
// console.log("Detail:", detail?.currentModel)
|
||||
})
|
||||
|
||||
widget.callback = () => {};
|
||||
|
||||
|
||||
// Note: If the node is collapsed, the draw method does not work and the canvas will not update.
|
||||
// const animator = () => {
|
||||
// threeCanvas.update(node.size[0]);
|
||||
@@ -240,12 +245,18 @@ app.registerExtension({
|
||||
|
||||
// Loading last model added
|
||||
if(currentModel){
|
||||
const [subfolder, name] = currentModel.split("/")
|
||||
const urlData = await api.apiURL(
|
||||
`/view?filename=${encodeURIComponent(
|
||||
name
|
||||
)}&type=input&subfolder=${subfolder}${app.getPreviewFormatParam()}${app.getRandParam()}`
|
||||
);
|
||||
|
||||
let urlData = ""
|
||||
if(currentModel.indexOf("assets/") === -1){
|
||||
const [subfolder, name] = currentModel.split("/")
|
||||
urlData = await api.apiURL(
|
||||
`/view?filename=${encodeURIComponent(
|
||||
name
|
||||
)}&type=input&subfolder=${subfolder}${app.getPreviewFormatParam()}${app.getRandParam()}`
|
||||
);
|
||||
} else {
|
||||
urlData = new URL(currentModel, import.meta.url).href
|
||||
}
|
||||
|
||||
await threeCnavasWidget.loaderGltf.load(urlData, ( glb ) => {
|
||||
threeCnavasWidget.addModel( glb );
|
||||
|
||||
+86
-3
@@ -12,6 +12,7 @@ from server import PromptServer
|
||||
import time
|
||||
from aiohttp import web
|
||||
import folder_paths
|
||||
import json
|
||||
|
||||
|
||||
# Message Handling
|
||||
@@ -42,14 +43,96 @@ import folder_paths
|
||||
DEBUG = True
|
||||
THREEVIEW_DICT = {} # ThreeView dict instances
|
||||
|
||||
# Directory threejs
|
||||
extension_threejs = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
# Support extension models
|
||||
support_ext = ("glb",)
|
||||
def getModelsInDir(folder, path):
|
||||
list_models = []
|
||||
for model in os.scandir(folder):
|
||||
if not model.is_file():
|
||||
continue
|
||||
|
||||
model_file_size = model.stat().st_size
|
||||
if model_file_size == 0:
|
||||
continue
|
||||
|
||||
model_file = model.name.split(".")
|
||||
ext = model_file[-1]
|
||||
|
||||
if ext not in support_ext:
|
||||
continue
|
||||
|
||||
pathFile = os.path.join(path, model.name)
|
||||
|
||||
# model_file_name = ".".join(model_file[:-1])
|
||||
list_models.append({"name": model.name, "size": model_file_size, "path": pathFile})
|
||||
|
||||
return list_models
|
||||
|
||||
|
||||
def createJsonFileStartup():
|
||||
models_js_dir = os.path.join(extension_threejs, "js")
|
||||
models_assets_dir = os.path.join(models_js_dir, "assets")
|
||||
|
||||
# Assets directory
|
||||
if not os.path.exists(models_assets_dir):
|
||||
return
|
||||
|
||||
models = {
|
||||
"models": {
|
||||
"default": getModelsInDir(models_assets_dir, "./assets/"),
|
||||
}
|
||||
}
|
||||
|
||||
saveFilePath = os.path.join(models_js_dir, "models_default.json")
|
||||
with open(saveFilePath, "w+", encoding="utf-8") as file:
|
||||
file.write(json.dumps(models, indent=4))
|
||||
|
||||
|
||||
# Create on startup default models json file in assets directory
|
||||
createJsonFileStartup()
|
||||
|
||||
|
||||
@PromptServer.instance.routes.get("/lth/models")
|
||||
async def getModels(request):
|
||||
try:
|
||||
models_assets_dir = os.path.join(extension_threejs, "js", "assets")
|
||||
models_input_dir = os.path.join(folder_paths.get_input_directory(), "ThreeViewModels")
|
||||
|
||||
models = {
|
||||
"default": [],
|
||||
"loaded": []
|
||||
}
|
||||
|
||||
# Assets directory
|
||||
if os.path.exists(models_assets_dir):
|
||||
models["default"] = getModelsInDir(models_assets_dir, "assets/")
|
||||
else:
|
||||
print(f"ThreeView: Path {models_assets_dir} not exists!")
|
||||
|
||||
# Input > ThreeViewModels directory
|
||||
if os.path.exists(models_input_dir):
|
||||
models["loaded"] = getModelsInDir(models_input_dir, "ThreeViewModels/")
|
||||
else:
|
||||
print(f"ThreeView: Path {models_input_dir} not exists!")
|
||||
|
||||
return web.json_response({"status":"ok", "models": models}, status=200)
|
||||
|
||||
except Exception as err:
|
||||
print("ThreeView: Get models error", err)
|
||||
return web.json_response({"status":"error"}, status=500)
|
||||
|
||||
@PromptServer.instance.routes.get("/api/lth/")
|
||||
async def indexPage(request):
|
||||
pathIndex = os.path.join(os.path.dirname(os.path.realpath(__file__)), "js", "index.html")
|
||||
pathIndex = os.path.join(extension_threejs, "js", "index.html")
|
||||
response = web.FileResponse(pathIndex)
|
||||
response.headers['Cache-Control'] = 'no-cache'
|
||||
response.headers["Pragma"] = "no-cache"
|
||||
response.headers["Expires"] = "0"
|
||||
return response
|
||||
return response
|
||||
|
||||
|
||||
@PromptServer.instance.routes.post("/lth/save_complete")
|
||||
async def checkSaveImage(request):
|
||||
@@ -61,7 +144,7 @@ async def checkSaveImage(request):
|
||||
return web.json_response({"status": "Complete"}, status=200)
|
||||
|
||||
return web.json_response({"status": "Error invalid unique_id"}, status=400)
|
||||
|
||||
|
||||
|
||||
def waitSaveImage(unique_id, time_out=100):
|
||||
for _ in range(time_out):
|
||||
|
||||
Reference in New Issue
Block a user