更新打包配置,完善组件入参模式,新增事件总线
This commit is contained in:
		
							
								
								
									
										26520
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										26520
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										19
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								package.json
									
									
									
									
									
								
							@@ -1,12 +1,13 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  "name": "material-editor",
 | 
					  "name": "material-editor",
 | 
				
			||||||
  "private": true,
 | 
					  "private": true,
 | 
				
			||||||
  "version": "1.0.0",
 | 
					  "version": "1.0.2",
 | 
				
			||||||
  "type": "module",
 | 
					  "type": "module",
 | 
				
			||||||
  "scripts": {
 | 
					  "scripts": {
 | 
				
			||||||
    "dev": "vite",
 | 
					    "dev": "vite",
 | 
				
			||||||
    "build": "vue-tsc -b && vite build",
 | 
					    "build": "vue-tsc -b && vite build",
 | 
				
			||||||
    "preview": "vite preview"
 | 
					    "preview": "vite preview",
 | 
				
			||||||
 | 
					    "prepublishOnly": "npm run build"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "files": [
 | 
					  "files": [
 | 
				
			||||||
    "dist",
 | 
					    "dist",
 | 
				
			||||||
@@ -15,13 +16,15 @@
 | 
				
			|||||||
    "index.html",
 | 
					    "index.html",
 | 
				
			||||||
    "README.md"
 | 
					    "README.md"
 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "module": "./dist/material-editor.js",
 | 
					  "module": "dist/material-editor.es.js",
 | 
				
			||||||
  "main": "./dist/material-editor.umd.js",
 | 
					  "main": "dist/material-editor.umd.js",
 | 
				
			||||||
  "exports": {
 | 
					  "exports": {
 | 
				
			||||||
    ".": {
 | 
					    ".": {
 | 
				
			||||||
      "import": "./dist/index.js",
 | 
					      "import": "./dist/material-editor.es.js",
 | 
				
			||||||
      "require": "./dist/index.cjs"
 | 
					      "require": "./dist/material-editor.umd.cjs"
 | 
				
			||||||
    }
 | 
					    },
 | 
				
			||||||
 | 
					    "./style.css": "./dist/material-editor.css",
 | 
				
			||||||
 | 
					    "./iife": "./dist/material-editor.iife.js"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "dependencies": {
 | 
					  "dependencies": {
 | 
				
			||||||
    "@jscad/modeling": "^2.11.0",
 | 
					    "@jscad/modeling": "^2.11.0",
 | 
				
			||||||
@@ -40,8 +43,10 @@
 | 
				
			|||||||
    "@types/node": "^22.14.1",
 | 
					    "@types/node": "^22.14.1",
 | 
				
			||||||
    "@vitejs/plugin-vue": "^5.2.3",
 | 
					    "@vitejs/plugin-vue": "^5.2.3",
 | 
				
			||||||
    "@vue/tsconfig": "^0.7.0",
 | 
					    "@vue/tsconfig": "^0.7.0",
 | 
				
			||||||
 | 
					    "sass-embedded": "^1.87.0",
 | 
				
			||||||
    "typescript": "~5.7.2",
 | 
					    "typescript": "~5.7.2",
 | 
				
			||||||
    "vite": "^6.2.6",
 | 
					    "vite": "^6.2.6",
 | 
				
			||||||
 | 
					    "vite-plugin-dts": "^4.5.3",
 | 
				
			||||||
    "vue-tsc": "^2.2.4"
 | 
					    "vue-tsc": "^2.2.4"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "packageManager": "pnpm@9.1.1+sha1.09ada6cd05003e0ced25fb716f9fda4063ec2e3b"
 | 
					  "packageManager": "pnpm@9.1.1+sha1.09ada6cd05003e0ced25fb716f9fda4063ec2e3b"
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										829
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										829
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -5,6 +5,7 @@ import { Viewer } from './Viewer';
 | 
				
			|||||||
import { PMREMGenerator3 } from './PMREMGenerator2';
 | 
					import { PMREMGenerator3 } from './PMREMGenerator2';
 | 
				
			||||||
import type { PhysicalMaterialRecord, TextureTableRecord } from 'webcad_ue4_api';
 | 
					import type { PhysicalMaterialRecord, TextureTableRecord } from 'webcad_ue4_api';
 | 
				
			||||||
import { MaterialEditorCameraControl } from './MaterialMouseControl';
 | 
					import { MaterialEditorCameraControl } from './MaterialMouseControl';
 | 
				
			||||||
 | 
					import { GetConfig } from '../lib/libOutputConfig';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function textureRenderUpdate(textureRecord:TextureTableRecord){
 | 
					async function textureRenderUpdate(textureRecord:TextureTableRecord){
 | 
				
			||||||
    const texture = textureRecord['texture'] as Texture;
 | 
					    const texture = textureRecord['texture'] as Texture;
 | 
				
			||||||
@@ -81,11 +82,6 @@ export class MaterialEditor extends Singleton
 | 
				
			|||||||
            this.Viewer.UpdateRender();
 | 
					            this.Viewer.UpdateRender();
 | 
				
			||||||
            this.Viewer.Fov = 90;
 | 
					            this.Viewer.Fov = 90;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        else
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            this.Canvas.appendChild(document);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    SetViewer(canvas: HTMLElement)
 | 
					    SetViewer(canvas: HTMLElement)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -119,8 +115,8 @@ export class MaterialEditor extends Singleton
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return new Promise(async (res, rej) =>
 | 
					        return new Promise(async (res, rej) =>
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            let urls = ['right.webp', 'left.webp', 'top.webp', 'bottom.webp', 'front.webp', 'back.webp'];
 | 
					            let urls = [...GetConfig().envTextureSrc];
 | 
				
			||||||
            new CubeTextureLoader().setPath('./')
 | 
					            new CubeTextureLoader()
 | 
				
			||||||
                .load(urls, (t) =>
 | 
					                .load(urls, (t) =>
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    t.encoding = sRGBEncoding;
 | 
					                    t.encoding = sRGBEncoding;
 | 
				
			||||||
@@ -148,8 +144,8 @@ export class MaterialEditor extends Singleton
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        this.exrPromise = new Promise<Texture>((res, rej) =>
 | 
					        this.exrPromise = new Promise<Texture>((res, rej) =>
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            let urls = ['right-gray.webp', 'left-gray.webp', 'top-gray.webp', 'bottom-gray.webp', 'front-gray.webp', 'back-gray.webp'];
 | 
					            let urls = [...GetConfig().grayEnvTextureSrc];
 | 
				
			||||||
            new CubeTextureLoader().setPath('./')
 | 
					            new CubeTextureLoader()
 | 
				
			||||||
                .load(urls, (t) =>
 | 
					                .load(urls, (t) =>
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    t.encoding = sRGBEncoding;
 | 
					                    t.encoding = sRGBEncoding;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,21 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
    <div vertical class="material-adjuster">
 | 
					    <div vertical class="material-adjuster">
 | 
				
			||||||
 | 
					        <div class="adjust-section">
 | 
				
			||||||
 | 
					            <h3>操作</h3>
 | 
				
			||||||
 | 
					            <fieldset v-if="debugMode" style="margin: 1em 0;">
 | 
				
			||||||
 | 
					                <legend>DEBUG</legend>
 | 
				
			||||||
 | 
					                <label>上传路径ID</label>
 | 
				
			||||||
 | 
					                <input v-model="materialInfo.dirId" type="text" placeholder="材质路径ID" />
 | 
				
			||||||
 | 
					            </fieldset>
 | 
				
			||||||
 | 
					            <label>材质名</label>
 | 
				
			||||||
 | 
					            <input v-model.trim="materialInfo.materialName" type="text" placeholder="材质名" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <!-- <CfFlex gap="1em">
 | 
				
			||||||
 | 
					                <button class="btn-success" style="min-width: 110px;" @click="HandleUpload">上传</button>
 | 
				
			||||||
 | 
					                <button class="btn-danger" style="min-width: 110px;" @click="HandleCancel">取消</button>
 | 
				
			||||||
 | 
					            </CfFlex> -->
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <div class="adjust-section">
 | 
					        <div class="adjust-section">
 | 
				
			||||||
            <h3>模型预览</h3>
 | 
					            <h3>模型预览</h3>
 | 
				
			||||||
            <label>选择模型样式</label>
 | 
					            <label>选择模型样式</label>
 | 
				
			||||||
@@ -10,8 +26,8 @@
 | 
				
			|||||||
        <div class="adjust-section" v-if="debugMode">
 | 
					        <div class="adjust-section" v-if="debugMode">
 | 
				
			||||||
            <h3>纹理选择(DEBUG)</h3>
 | 
					            <h3>纹理选择(DEBUG)</h3>
 | 
				
			||||||
            <label>纹理链接</label>
 | 
					            <label>纹理链接</label>
 | 
				
			||||||
            <input v-model.trim="textureLink" type="text" placeholder="在此键入纹理图像的URL..." />
 | 
					            <input v-model.trim="_textureSrc" type="text" placeholder="在此键入纹理图像的URL..." />
 | 
				
			||||||
            <button class="btn-primary" @click="scene.ChangeTextureAsync(textureLink)"
 | 
					            <button class="btn-primary" @click="scene.ChangeTextureAsync(_textureSrc)"
 | 
				
			||||||
                style="margin-left: 1em;">应用</button>
 | 
					                style="margin-left: 1em;">应用</button>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        <div class="adjust-section">
 | 
					        <div class="adjust-section">
 | 
				
			||||||
@@ -85,39 +101,41 @@
 | 
				
			|||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
 | 
					 | 
				
			||||||
        <div class="adjust-section">
 | 
					 | 
				
			||||||
            <h3>操作</h3>
 | 
					 | 
				
			||||||
            <fieldset v-if="debugMode" style="margin: 1em 0;">
 | 
					 | 
				
			||||||
                <legend>DEBUG</legend>
 | 
					 | 
				
			||||||
                <label>上传路径ID</label>
 | 
					 | 
				
			||||||
                <input v-model="materialRequest.dirId" type="text" placeholder="材质路径ID" />
 | 
					 | 
				
			||||||
            </fieldset>
 | 
					 | 
				
			||||||
            <label>材质名</label>
 | 
					 | 
				
			||||||
            <input v-model="materialRequest.materialName" type="text" placeholder="材质名" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <CfFlex gap="1em">
 | 
					 | 
				
			||||||
                <button class="btn-success" style="min-width: 110px;" @click="HandleUpload">上传</button>
 | 
					 | 
				
			||||||
                <button class="btn-danger" style="min-width: 110px;" @click="HandleCancel">取消</button>
 | 
					 | 
				
			||||||
            </CfFlex>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script setup lang='ts'>
 | 
					<script setup lang='ts'>
 | 
				
			||||||
import { ref, reactive, watch } from "vue"
 | 
					import { ref, reactive, watch, onMounted } from "vue"
 | 
				
			||||||
import { useScene, type TextureAdjustment } from "../stores/sceneStore";
 | 
					import { useScene, type TextureAdjustment } from "../stores/sceneStore";
 | 
				
			||||||
import { storeToRefs } from "pinia";
 | 
					import { storeToRefs } from "pinia";
 | 
				
			||||||
import CfFlex from "./CfFlex.vue";
 | 
					import CfFlex from "./CfFlex.vue";
 | 
				
			||||||
import { DirectoryId } from "../api/Request";
 | 
					import { DirectoryId } from "../api/Request";
 | 
				
			||||||
import { IsNullOrWhitespace } from "../helpers/helper.string";
 | 
					import { IsNullOrWhitespace } from "../helpers/helper.string";
 | 
				
			||||||
 | 
					import { DeflateAsync } from "../helpers/helper.compression";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface MaterialRequest {
 | 
				
			||||||
 | 
					    /** 材质名 */
 | 
				
			||||||
 | 
					    name: string;
 | 
				
			||||||
 | 
					    /** 材质logo文件 */
 | 
				
			||||||
 | 
					    logo: Blob;
 | 
				
			||||||
 | 
					    /** 序列化并Deflate压缩后的材质文件的Base64编码 */
 | 
				
			||||||
 | 
					    file: string;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const scene = useScene();
 | 
					const scene = useScene();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const debugMode = ref(true);
 | 
					const props = defineProps<{
 | 
				
			||||||
 | 
					    textureSrc?: string;
 | 
				
			||||||
 | 
					}>();
 | 
				
			||||||
 | 
					const emits = defineEmits<{
 | 
				
			||||||
 | 
					    (e: 'cancel'): void;
 | 
				
			||||||
 | 
					    (e: 'submit', data: MaterialRequest): void; 
 | 
				
			||||||
 | 
					}>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const debugMode = ref(false);
 | 
				
			||||||
const { CurrGeometry, Geometries, Material } = storeToRefs(scene);
 | 
					const { CurrGeometry, Geometries, Material } = storeToRefs(scene);
 | 
				
			||||||
const enableTexture = ref(Material.value.useMap);
 | 
					const enableTexture = ref(Material.value.useMap);
 | 
				
			||||||
const textureLink = ref('');
 | 
					const _textureSrc = ref(props.textureSrc);
 | 
				
			||||||
const textureAdjustment = reactive<TextureAdjustment>({
 | 
					const textureAdjustment = reactive<TextureAdjustment>({
 | 
				
			||||||
    wrapS: 0,
 | 
					    wrapS: 0,
 | 
				
			||||||
    wrapT: 0,
 | 
					    wrapT: 0,
 | 
				
			||||||
@@ -134,9 +152,18 @@ const model = reactive({
 | 
				
			|||||||
    normalScale: Material.value.bumpScale,
 | 
					    normalScale: Material.value.bumpScale,
 | 
				
			||||||
    emissiveIntensity: Material.value.specular
 | 
					    emissiveIntensity: Material.value.specular
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
const materialRequest = reactive({
 | 
					const materialInfo = reactive({
 | 
				
			||||||
    dirId: DirectoryId.MaterialDir, // 正常来说是2
 | 
					    dirId: DirectoryId.MaterialDir, // 正常来说是2
 | 
				
			||||||
    materialName: ''
 | 
					    materialName: '材质1'
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					onMounted(() => {
 | 
				
			||||||
 | 
					    scene.ChangeTextureAsync(_textureSrc.value);
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					watch(() => props.textureSrc, async (val) => {
 | 
				
			||||||
 | 
					    _textureSrc.value = val;
 | 
				
			||||||
 | 
					    await scene.ChangeTextureAsync(_textureSrc.value);
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
watch(model, async (val) => {
 | 
					watch(model, async (val) => {
 | 
				
			||||||
@@ -171,25 +198,34 @@ async function EnableTexture(enable: boolean) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
async function HandleUpload() {
 | 
					async function HandleUpload() {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
        if (IsNullOrWhitespace(materialRequest.materialName)) {
 | 
					        if (IsNullOrWhitespace(materialInfo.materialName)) {
 | 
				
			||||||
            alert('材质名称不可为空');
 | 
					            alert('材质名称不可为空');
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        uploading.value = true;
 | 
					        uploading.value = true;
 | 
				
			||||||
        await scene.UploadMaterialAsync(materialRequest);
 | 
					        const data = {
 | 
				
			||||||
 | 
					            name: materialInfo.materialName,
 | 
				
			||||||
 | 
					            logo: await scene.GenerateMaterialLogoAsync(),
 | 
				
			||||||
 | 
					            // jsonString -> Deflate -> BinaryString -> Base64
 | 
				
			||||||
 | 
					            file: btoa(String.fromCharCode(...await DeflateAsync(await scene.SerializeMaterialAsync())))
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        emits('submit', data);
 | 
				
			||||||
 | 
					        return data;
 | 
				
			||||||
 | 
					    } finally {
 | 
				
			||||||
        uploading.value = false;
 | 
					        uploading.value = false;
 | 
				
			||||||
        alert("上传成功");
 | 
					 | 
				
			||||||
    } catch (error) {
 | 
					 | 
				
			||||||
        console.error(error);
 | 
					 | 
				
			||||||
        alert("上传材质出错,请检查网络连接或联系管理员");
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function HandleCancel() {
 | 
					function HandleCancel() {
 | 
				
			||||||
    // TODO: 触发取消事件
 | 
					    emits('cancel');
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					defineExpose({
 | 
				
			||||||
 | 
					    Upload: HandleUpload,
 | 
				
			||||||
 | 
					    Cancel: HandleCancel
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style scoped>
 | 
					<style scoped>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,35 +1,59 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
    <CfFlex class="material-view">
 | 
					    <CfFlex class="material-view">
 | 
				
			||||||
        <div ref="container" style="width: 100%; height: 100%; flex: 3; box-sizing: border-box;" />
 | 
					        <div ref="container" class="material-view-container" />
 | 
				
			||||||
        <MaterialAdjuster style="flex: 1;overflow-y: auto; width: 100%; height: 100%; box-sizing: border-box;" />
 | 
					        <MaterialAdjuster ref="adjuster" class="material-view-sider" :texture-src="textureSrc" @cancel="config.cancelCallback" @submit="config.submitCallback" />
 | 
				
			||||||
    </CfFlex>
 | 
					    </CfFlex>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
import { onMounted, useTemplateRef } from 'vue';
 | 
					import { onMounted, ref, useTemplateRef } from 'vue';
 | 
				
			||||||
import MaterialAdjuster from './MaterialAdjuster.vue';
 | 
					import MaterialAdjuster from './MaterialAdjuster.vue';
 | 
				
			||||||
import { useScene } from '../stores/sceneStore';
 | 
					import { useScene } from '../stores/sceneStore';
 | 
				
			||||||
import CfFlex from './CfFlex.vue';
 | 
					import CfFlex from './CfFlex.vue';
 | 
				
			||||||
 | 
					import { GetConfig } from '../lib/libOutputConfig';
 | 
				
			||||||
 | 
					import { useEvent } from '../stores/eventStore';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const scene = useScene();
 | 
					const scene = useScene();
 | 
				
			||||||
const container = useTemplateRef<HTMLElement>('container');
 | 
					const eventbus = useEvent();
 | 
				
			||||||
 | 
					const container = useTemplateRef('container');
 | 
				
			||||||
 | 
					const adjusterRef = useTemplateRef('adjuster');
 | 
				
			||||||
 | 
					const config = GetConfig();
 | 
				
			||||||
 | 
					const textureSrc = ref(config.textureSrc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 禁用右键菜单
 | 
					// 禁用右键菜单
 | 
				
			||||||
document.addEventListener('contextmenu', (e) => e.preventDefault()); 
 | 
					document.addEventListener('contextmenu', (e) => e.preventDefault()); 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
onMounted(() => {
 | 
					onMounted(() => {
 | 
				
			||||||
    scene.Initial(container.value);
 | 
					    scene.Initial(container.value);
 | 
				
			||||||
 | 
					    eventbus.Subscribe('submit', () => adjusterRef.value.Upload());
 | 
				
			||||||
 | 
					    eventbus.Subscribe('update-texture', () => {
 | 
				
			||||||
 | 
					        textureSrc.value = config.textureSrc
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style scoped>
 | 
					<style scoped lang="scss">
 | 
				
			||||||
.material-view
 | 
					.material-view
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    width: 100%;
 | 
					    width: 100%;
 | 
				
			||||||
    height: 100vh;
 | 
					    height: 100%;
 | 
				
			||||||
    box-sizing: border-box;
 | 
					    box-sizing: border-box;
 | 
				
			||||||
    padding: 0;
 | 
					    padding: 0;
 | 
				
			||||||
    margin: 0;
 | 
					    margin: 0;
 | 
				
			||||||
    overflow: hidden;
 | 
					    overflow: hidden;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    &-container {
 | 
				
			||||||
 | 
					        flex: 3 1; 
 | 
				
			||||||
 | 
					        height: 100%; 
 | 
				
			||||||
 | 
					        box-sizing: border-box;
 | 
				
			||||||
 | 
					        overflow: hidden;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    &-sider {
 | 
				
			||||||
 | 
					        flex: 1 1;
 | 
				
			||||||
 | 
					        overflow-y: auto; 
 | 
				
			||||||
 | 
					        height: 100%; 
 | 
				
			||||||
 | 
					        box-sizing: border-box;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
</style>
 | 
					</style>
 | 
				
			||||||
							
								
								
									
										43
									
								
								src/lib/entry.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/lib/entry.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
				
			|||||||
 | 
					import { createApp, type App as VueApp } from 'vue'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import App from '../App.vue'
 | 
				
			||||||
 | 
					import '../assets/main.css'
 | 
				
			||||||
 | 
					import { createPinia } from 'pinia';
 | 
				
			||||||
 | 
					import { ConfigureLibOutput, type LibOutputConfig } from './libOutputConfig';
 | 
				
			||||||
 | 
					import { useEvent } from '../stores/eventStore';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let app: VueApp<Element> = undefined;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 挂载材质编辑器到指定元素
 | 
				
			||||||
 | 
					*  @param element 要挂载的HTML元素
 | 
				
			||||||
 | 
					 * @param options 程序配置
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export function Mount(element: Element, options: Partial<LibOutputConfig>) {
 | 
				
			||||||
 | 
					    ConfigureLibOutput(options);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const pinia = createPinia();
 | 
				
			||||||
 | 
					    app = createApp(App);
 | 
				
			||||||
 | 
					    app.use(pinia)
 | 
				
			||||||
 | 
					        .mount(element);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** 对程序配置进行更改 */
 | 
				
			||||||
 | 
					export function Configure(options: Partial<LibOutputConfig>) {
 | 
				
			||||||
 | 
					    ConfigureLibOutput(options);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** 卸载已经挂载的DOM */
 | 
				
			||||||
 | 
					export function Unmount() {
 | 
				
			||||||
 | 
					    app.unmount();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** 手动进行材质提交 */
 | 
				
			||||||
 | 
					export function Submit() {
 | 
				
			||||||
 | 
					    useEvent().Publish('submit');
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** 更新材质贴图 */
 | 
				
			||||||
 | 
					export function UpdateTexture() {
 | 
				
			||||||
 | 
					    useEvent().Publish('update-texture');
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,5 +1,9 @@
 | 
				
			|||||||
import MaterialView from "../components/MaterialView.vue";
 | 
					import { Mount, Unmount, Submit, UpdateTexture, Configure } from "./entry";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export {
 | 
					export {
 | 
				
			||||||
    MaterialView
 | 
					    Mount,
 | 
				
			||||||
 | 
					    Unmount,
 | 
				
			||||||
 | 
					    Submit,
 | 
				
			||||||
 | 
					    UpdateTexture,
 | 
				
			||||||
 | 
					    Configure
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										38
									
								
								src/lib/libOutputConfig.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/lib/libOutputConfig.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,38 @@
 | 
				
			|||||||
 | 
					import type { DeepReadonly } from "vue"
 | 
				
			||||||
 | 
					import type { MaterialRequest } from "../components/MaterialAdjuster.vue"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let _libOutputConfig = {
 | 
				
			||||||
 | 
					    textureSrc: "",
 | 
				
			||||||
 | 
					    submitCallback: undefined,
 | 
				
			||||||
 | 
					    cancelCallback: undefined,
 | 
				
			||||||
 | 
					    envTextureSrc: ['./right.webp', './left.webp', './top.webp', './bottom.webp', './front.webp', './back.webp'],
 | 
				
			||||||
 | 
					    grayEnvTextureSrc: ['./right-gray.webp', './left-gray.webp', './top-gray.webp', './bottom-gray.webp', './front-gray.webp', './back-gray.webp'],
 | 
				
			||||||
 | 
					} as LibOutputConfig
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function ConfigureLibOutput(config: Partial<LibOutputConfig>) {
 | 
				
			||||||
 | 
					    Object.assign(_libOutputConfig, {
 | 
				
			||||||
 | 
					        ...config
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    // _libOutputConfig = config;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type LibOutputConfig = {
 | 
				
			||||||
 | 
					    /** 材质贴图链接 */
 | 
				
			||||||
 | 
					    textureSrc: string,
 | 
				
			||||||
 | 
					    /** 环境贴图链接(立方体贴图,按照顺序输入[右左上下前后]) */
 | 
				
			||||||
 | 
					    envTextureSrc: string[],
 | 
				
			||||||
 | 
					    /** 灰度环境贴图链接,输入格式与环境贴图一致 */
 | 
				
			||||||
 | 
					    grayEnvTextureSrc: string[],
 | 
				
			||||||
 | 
					    /** 提交材质时的回调 */
 | 
				
			||||||
 | 
					    submitCallback?: (data: MaterialRequest) => void,
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 取消材质编辑时的回调
 | 
				
			||||||
 | 
					     * @deprecated 不需要使用
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    cancelCallback?: () => void
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function GetConfig(): DeepReadonly<typeof _libOutputConfig>
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    return _libOutputConfig;
 | 
				
			||||||
 | 
					} 
 | 
				
			||||||
							
								
								
									
										18
									
								
								src/stores/eventStore.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/stores/eventStore.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
				
			|||||||
 | 
					import { defineStore } from "pinia";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const useEvent = defineStore('event', () => {
 | 
				
			||||||
 | 
					    const events: Partial<Record<EventNames, Function[]>> = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function Subscribe(name: EventNames, callback: Function) {
 | 
				
			||||||
 | 
					        events[name] = events[name] || [];
 | 
				
			||||||
 | 
					        events[name].push(callback);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    function Publish(name: EventNames, ...args: any[]) {
 | 
				
			||||||
 | 
					        events[name]?.forEach(e => e(...args));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return { Subscribe, Publish };
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type EventNames = 'submit' | 'update-texture'
 | 
				
			||||||
@@ -1,14 +1,11 @@
 | 
				
			|||||||
import { defineStore } from "pinia";
 | 
					import { defineStore } from "pinia";
 | 
				
			||||||
import { computed, ref, watch } from "vue";
 | 
					import { computed, ref } from "vue";
 | 
				
			||||||
import { MaterialEditor } from "../common/MaterialEditor";
 | 
					import { MaterialEditor } from "../common/MaterialEditor";
 | 
				
			||||||
import { Database, ObjectId, PhysicalMaterialRecord, TextureTableRecord } from "webcad_ue4_api";
 | 
					import { ObjectId, PhysicalMaterialRecord, TextureTableRecord } from "webcad_ue4_api";
 | 
				
			||||||
import { LoadImageFromUrl } from "../helpers/helper.imageLoader";
 | 
					import { LoadImageFromUrl } from "../helpers/helper.imageLoader";
 | 
				
			||||||
import { Texture } from "three";
 | 
					import { Texture } from "three";
 | 
				
			||||||
import { materialRenderer } from "../common/MaterialRenderer";
 | 
					import { materialRenderer } from "../common/MaterialRenderer";
 | 
				
			||||||
import { ImgsUrl, MaterialUrls } from "../api/Api";
 | 
					 | 
				
			||||||
import { Post, PostJson, RequestStatus } from "../api/Request";
 | 
					 | 
				
			||||||
import { MaterialOut } from "../common/MaterialSerializer";
 | 
					import { MaterialOut } from "../common/MaterialSerializer";
 | 
				
			||||||
import { DeflateAsync } from "../helpers/helper.compression";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const useScene = defineStore('scene', () => {
 | 
					export const useScene = defineStore('scene', () => {
 | 
				
			||||||
    let _editor: MaterialEditor;
 | 
					    let _editor: MaterialEditor;
 | 
				
			||||||
@@ -72,7 +69,7 @@ export const useScene = defineStore('scene', () => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        _currTexture.value = record['texture'] as Texture;
 | 
					        _currTexture.value = record['texture'] as Texture;
 | 
				
			||||||
        _currTexture.value.image = img;
 | 
					        _currTexture.value.image = img;
 | 
				
			||||||
        Material.value.map = record.Id;
 | 
					        Material.value.map = img ? record.Id : undefined;
 | 
				
			||||||
        _currTexture.value.needsUpdate = true;
 | 
					        _currTexture.value.needsUpdate = true;
 | 
				
			||||||
        await UpdateMaterialAsync();
 | 
					        await UpdateMaterialAsync();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -90,31 +87,28 @@ export const useScene = defineStore('scene', () => {
 | 
				
			|||||||
        Update();
 | 
					        Update();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    interface UploadMaterialRequest {
 | 
					    async function SerializeMaterialAsync() {
 | 
				
			||||||
        dirId: string;
 | 
					 | 
				
			||||||
        materialName: string;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    async function UploadMaterialAsync(request: UploadMaterialRequest) {
 | 
					 | 
				
			||||||
        // TODO: Warn: 是否要生成logo路径?
 | 
					        // TODO: Warn: 是否要生成logo路径?
 | 
				
			||||||
        // const logoPath = await HandleUpdateLogo(); 
 | 
					        // const logoPath = await HandleUpdateLogo(); 
 | 
				
			||||||
        const matJson = MaterialOut(Material.value as PhysicalMaterialRecord);
 | 
					        const matJson = MaterialOut(Material.value as PhysicalMaterialRecord);
 | 
				
			||||||
        console.log(matJson);
 | 
					        return matJson;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async function HandleUpdateLogo() {
 | 
					    async function GenerateMaterialLogoAsync() {
 | 
				
			||||||
        const blob = await materialRenderer.getBlob(Material.value.Material);
 | 
					        const blob = await materialRenderer.getBlob(Material.value.Material);
 | 
				
			||||||
        const file = new File([blob], "blob.png", { type: blob.type });
 | 
					        return blob;
 | 
				
			||||||
 | 
					        // const file = new File([blob], "blob.png", { type: blob.type });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const formData = new FormData();
 | 
					        // const formData = new FormData();
 | 
				
			||||||
        formData.append("file", file);
 | 
					        // formData.append("file", file);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let data = await Post(ImgsUrl.logo, formData);
 | 
					        // let data = await Post(ImgsUrl.logo, formData);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let logoPath = "";
 | 
					        // let logoPath = "";
 | 
				
			||||||
        if (data.err_code === RequestStatus.Ok) {
 | 
					        // if (data.err_code === RequestStatus.Ok) {
 | 
				
			||||||
            logoPath = data.images.path;
 | 
					        //     logoPath = data.images.path;
 | 
				
			||||||
        }
 | 
					        // }
 | 
				
			||||||
        return logoPath;
 | 
					        // return logoPath;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return {
 | 
					    return {
 | 
				
			||||||
@@ -126,7 +120,8 @@ export const useScene = defineStore('scene', () => {
 | 
				
			|||||||
        UpdateMaterialAsync,
 | 
					        UpdateMaterialAsync,
 | 
				
			||||||
        ChangeTextureAsync,
 | 
					        ChangeTextureAsync,
 | 
				
			||||||
        UpdateTexture,
 | 
					        UpdateTexture,
 | 
				
			||||||
        UploadMaterialAsync,
 | 
					        SerializeMaterialAsync,
 | 
				
			||||||
 | 
					        GenerateMaterialLogoAsync
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -139,3 +134,7 @@ export type TextureAdjustment = {
 | 
				
			|||||||
    moveX: number,
 | 
					    moveX: number,
 | 
				
			||||||
    moveY: number
 | 
					    moveY: number
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					export interface UploadMaterialRequest {
 | 
				
			||||||
 | 
					    dirId: string;
 | 
				
			||||||
 | 
					    materialName: string;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -10,5 +10,5 @@
 | 
				
			|||||||
    // "noFallthroughCasesInSwitch": true,
 | 
					    // "noFallthroughCasesInSwitch": true,
 | 
				
			||||||
    // "noUncheckedSideEffectImports": true
 | 
					    // "noUncheckedSideEffectImports": true
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]
 | 
					  "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue", "src/lib/index.ts"]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										10
									
								
								tsconfig.build.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								tsconfig.build.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					    "extends": "./tsconfig.json",
 | 
				
			||||||
 | 
					    "compilerOptions": {
 | 
				
			||||||
 | 
					        "declaration": true,
 | 
				
			||||||
 | 
					        "emitDeclarationOnly": true,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "include": [
 | 
				
			||||||
 | 
					        "src"
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,5 +1,6 @@
 | 
				
			|||||||
import { defineConfig } from 'vite'
 | 
					import { defineConfig } from 'vite'
 | 
				
			||||||
import vue from '@vitejs/plugin-vue'
 | 
					import vue from '@vitejs/plugin-vue'
 | 
				
			||||||
 | 
					import dts from 'vite-plugin-dts';
 | 
				
			||||||
import { dirname, resolve } from 'node:path'
 | 
					import { dirname, resolve } from 'node:path'
 | 
				
			||||||
import { fileURLToPath } from 'node:url'
 | 
					import { fileURLToPath } from 'node:url'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -7,20 +8,29 @@ const __dirname = dirname(fileURLToPath(import.meta.url))
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// https://vite.dev/config/
 | 
					// https://vite.dev/config/
 | 
				
			||||||
export default defineConfig({
 | 
					export default defineConfig({
 | 
				
			||||||
  plugins: [vue()],
 | 
					  plugins: [vue(), dts({rollupTypes: true, tsconfigPath: './tsconfig.app.json',insertTypesEntry: true})],
 | 
				
			||||||
  build: {
 | 
					  build: {
 | 
				
			||||||
    lib: {
 | 
					    lib: {
 | 
				
			||||||
      entry: resolve(__dirname, 'src/lib/index.ts'),
 | 
					      entry: resolve(__dirname, 'src/lib/index.ts'),
 | 
				
			||||||
      name: 'MaterialEditor',
 | 
					      name: 'MaterialEditor',
 | 
				
			||||||
      fileName: (format) => `material-editor.${format}.js`
 | 
					      fileName: (format) => `material-editor.${format}.js`,
 | 
				
			||||||
 | 
					      formats: ['es']
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    rollupOptions: {
 | 
					    rollupOptions: {
 | 
				
			||||||
      external: ['vue'],
 | 
					      // external: ['vue'],
 | 
				
			||||||
      output: {
 | 
					      output: {
 | 
				
			||||||
        globals: {
 | 
					        globals: {
 | 
				
			||||||
          vue: 'Vue'
 | 
					          vue: 'Vue'
 | 
				
			||||||
        }
 | 
					        },
 | 
				
			||||||
      }
 | 
					        // manualChunks: (id) => {
 | 
				
			||||||
 | 
					        //   if (id.includes('node_modules'))
 | 
				
			||||||
 | 
					        //   {
 | 
				
			||||||
 | 
					        //     if(/three/.test(id))
 | 
				
			||||||
 | 
					        //       return 'three';
 | 
				
			||||||
 | 
					        //   }
 | 
				
			||||||
 | 
					        //   return null;
 | 
				
			||||||
 | 
					        // }
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user