添加编辑模式
This commit is contained in:
		@@ -32,6 +32,7 @@
 | 
			
		||||
    "fflate": "^0.8.2",
 | 
			
		||||
    "flatbush": "^3.3.0",
 | 
			
		||||
    "js-angusj-clipper": "^1.2.1",
 | 
			
		||||
    "pako": "^2.1.0",
 | 
			
		||||
    "pinia": "^3.0.2",
 | 
			
		||||
    "polylabel": "^1.1.0",
 | 
			
		||||
    "three": "npm:three-cf@^0.122.9",
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										8
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										8
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							@@ -23,6 +23,9 @@ importers:
 | 
			
		||||
      js-angusj-clipper:
 | 
			
		||||
        specifier: ^1.2.1
 | 
			
		||||
        version: 1.3.1
 | 
			
		||||
      pako:
 | 
			
		||||
        specifier: ^2.1.0
 | 
			
		||||
        version: 2.1.0
 | 
			
		||||
      pinia:
 | 
			
		||||
        specifier: ^3.0.2
 | 
			
		||||
        version: 3.0.2(typescript@5.7.3)(vue@3.5.13(typescript@5.7.3))
 | 
			
		||||
@@ -703,6 +706,9 @@ packages:
 | 
			
		||||
    engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
 | 
			
		||||
    hasBin: true
 | 
			
		||||
 | 
			
		||||
  pako@2.1.0:
 | 
			
		||||
    resolution: {integrity: sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==}
 | 
			
		||||
 | 
			
		||||
  path-browserify@1.0.1:
 | 
			
		||||
    resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==}
 | 
			
		||||
 | 
			
		||||
@@ -1620,6 +1626,8 @@ snapshots:
 | 
			
		||||
 | 
			
		||||
  nanoid@3.3.11: {}
 | 
			
		||||
 | 
			
		||||
  pako@2.1.0: {}
 | 
			
		||||
 | 
			
		||||
  path-browserify@1.0.1: {}
 | 
			
		||||
 | 
			
		||||
  path-parse@1.0.7: {}
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,7 @@
 | 
			
		||||
            <h3>纹理选择(DEBUG)</h3>
 | 
			
		||||
            <label>纹理链接</label>
 | 
			
		||||
            <input v-model.trim="_textureSrc" type="text" placeholder="在此键入纹理图像的URL..." />
 | 
			
		||||
            <button class="btn-primary" @click="scene.ChangeTextureAsync(_textureSrc)"
 | 
			
		||||
            <button class="btn-primary" @click="scene.ChangeTextureFromUrlAsync(_textureSrc)"
 | 
			
		||||
                style="margin-left: 1em;">应用</button>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="adjust-section">
 | 
			
		||||
@@ -112,6 +112,7 @@ import CfFlex from "./CfFlex.vue";
 | 
			
		||||
import { DirectoryId } from "../api/Request";
 | 
			
		||||
import { IsNullOrWhitespace } from "../helpers/helper.string";
 | 
			
		||||
import { DeflateAsync } from "../helpers/helper.compression";
 | 
			
		||||
import { ToDeflatedBase64 } from "../helpers/helper.material";
 | 
			
		||||
 | 
			
		||||
export interface MaterialRequest {
 | 
			
		||||
    /** 材质名 */
 | 
			
		||||
@@ -136,7 +137,7 @@ const debugMode = ref(false);
 | 
			
		||||
const { CurrGeometry, Geometries, Material } = storeToRefs(scene);
 | 
			
		||||
const enableTexture = ref(Material.value.useMap);
 | 
			
		||||
const _textureSrc = ref(props.textureSrc);
 | 
			
		||||
const textureAdjustment = reactive<TextureAdjustment>({
 | 
			
		||||
const textureAdjustment = ref<TextureAdjustment>({
 | 
			
		||||
    wrapS: 0,
 | 
			
		||||
    wrapT: 0,
 | 
			
		||||
    rotation: 0,
 | 
			
		||||
@@ -158,12 +159,12 @@ const materialInfo = reactive({
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
    scene.ChangeTextureAsync(_textureSrc.value);
 | 
			
		||||
    scene.ChangeTextureFromUrlAsync(_textureSrc.value);
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
watch(() => props.textureSrc, async (val) => {
 | 
			
		||||
    _textureSrc.value = val;
 | 
			
		||||
    await scene.ChangeTextureAsync(_textureSrc.value);
 | 
			
		||||
    await scene.ChangeTextureFromUrlAsync(_textureSrc.value);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
watch(model, async (val) => {
 | 
			
		||||
@@ -178,6 +179,19 @@ watch(textureAdjustment, async (val) => {
 | 
			
		||||
    UpdateTexture();
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// 监听纹理更新
 | 
			
		||||
watch(() => scene.CurrTexture, (val) => {
 | 
			
		||||
    textureAdjustment.value = {
 | 
			
		||||
        wrapS: val.wrapS,
 | 
			
		||||
        wrapT: val.wrapT,
 | 
			
		||||
        rotation: val.rotation,
 | 
			
		||||
        repeatX: val.repeat.x,
 | 
			
		||||
        repeatY: val.repeat.y,
 | 
			
		||||
        moveX: val.offset.x,        
 | 
			
		||||
        moveY: val.offset.y
 | 
			
		||||
    }
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
async function UpdateMaterial() {
 | 
			
		||||
    Material.value.matalness = model.metallic;
 | 
			
		||||
    Material.value.roughness = model.roughness;
 | 
			
		||||
@@ -188,7 +202,17 @@ async function UpdateMaterial() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function UpdateTexture() {
 | 
			
		||||
    scene.UpdateTexture(textureAdjustment);
 | 
			
		||||
    const texture = scene.CurrTexture;
 | 
			
		||||
    const val = textureAdjustment.value;
 | 
			
		||||
    texture.wrapS = val.wrapS;
 | 
			
		||||
    texture.wrapT = val.wrapT;
 | 
			
		||||
    texture.anisotropy = 16;
 | 
			
		||||
    texture.rotation = val.rotation;
 | 
			
		||||
    texture.repeat.set(val.repeatX, val.repeatY);
 | 
			
		||||
    texture.offset.set(val.moveX, val.moveY);
 | 
			
		||||
    texture.needsUpdate = true;
 | 
			
		||||
 | 
			
		||||
    scene.Update();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function EnableTexture(enable: boolean) {
 | 
			
		||||
@@ -208,7 +232,7 @@ async function HandleUpload() {
 | 
			
		||||
            name: materialInfo.materialName,
 | 
			
		||||
            logo: await scene.GenerateMaterialLogoAsync(),
 | 
			
		||||
            // jsonString -> Deflate -> BinaryString -> Base64
 | 
			
		||||
            file: btoa(String.fromCharCode(...await DeflateAsync(await scene.SerializeMaterialAsync())))
 | 
			
		||||
            file: ToDeflatedBase64(await scene.SerializeMaterialAsync())
 | 
			
		||||
        };
 | 
			
		||||
        emits('submit', data);
 | 
			
		||||
        return data;
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,7 @@ import { useScene } from '../stores/sceneStore';
 | 
			
		||||
import CfFlex from './CfFlex.vue';
 | 
			
		||||
import { GetConfig } from '../lib/libOutputConfig';
 | 
			
		||||
import { useEvent } from '../stores/eventStore';
 | 
			
		||||
import { FromDeflateBase64 } from '../helpers/helper.material';
 | 
			
		||||
 | 
			
		||||
const scene = useScene();
 | 
			
		||||
const eventbus = useEvent();
 | 
			
		||||
@@ -21,10 +22,13 @@ const textureSrc = ref(config.textureSrc);
 | 
			
		||||
 | 
			
		||||
// 禁用右键菜单
 | 
			
		||||
document.addEventListener('contextmenu', (e) => e.preventDefault()); 
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
onMounted(async () => {
 | 
			
		||||
    scene.Initial(container.value);
 | 
			
		||||
    await HandleUpdateConfig();
 | 
			
		||||
 | 
			
		||||
    eventbus.Subscribe('submit', HandleUpload);
 | 
			
		||||
    eventbus.Subscribe('update-texture', HandleChangeTexture);
 | 
			
		||||
    eventbus.Subscribe('update-config', HandleUpdateConfig)
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
onBeforeUnmount(() => {
 | 
			
		||||
@@ -41,6 +45,15 @@ function HandleChangeTexture() {
 | 
			
		||||
    textureSrc.value = config.textureSrc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function HandleUpdateConfig() {
 | 
			
		||||
    if (config.file && config.file.length > 0) {
 | 
			
		||||
        console.log("base64", config.file);
 | 
			
		||||
        const json = FromDeflateBase64(config.file);
 | 
			
		||||
        await scene.ImportMaterialAsync(json);
 | 
			
		||||
    }
 | 
			
		||||
    textureSrc.value = config.textureSrc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style scoped lang="scss">
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										15
									
								
								src/helpers/helper.material.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/helpers/helper.material.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
import Pako from "pako";
 | 
			
		||||
import { Decompress, DecompressAsync, Deflate, DeflateAsync, StrToU8, U8ToStr } from "./helper.compression";
 | 
			
		||||
 | 
			
		||||
export function ToDeflatedBase64(materialJson: string)
 | 
			
		||||
{
 | 
			
		||||
    return btoa(String.fromCharCode(...Deflate(materialJson)))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function FromDeflateBase64(base64: string) {
 | 
			
		||||
    let binaryString = atob(base64);
 | 
			
		||||
    console.log("Bin", binaryString);
 | 
			
		||||
    let data = Pako.inflate(binaryString as any, { to: "string" });
 | 
			
		||||
    console.log("data", data);
 | 
			
		||||
    return data;
 | 
			
		||||
}
 | 
			
		||||
@@ -27,6 +27,7 @@ export function Mount(element: Element, options: Partial<LibOutputConfig>) {
 | 
			
		||||
/** 对程序配置进行更改 */
 | 
			
		||||
export function Configure(options: Partial<LibOutputConfig>) {
 | 
			
		||||
    ConfigureLibOutput(options);
 | 
			
		||||
    useEvent().Publish('update-config');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 卸载已经挂载的DOM */
 | 
			
		||||
@@ -52,7 +53,7 @@ export async function SubmitRawAsync(options: { textureSrc: string }): Promise<{
 | 
			
		||||
    const virtualDom = document.createElement('div');
 | 
			
		||||
    virtualDom.style.display = 'none';
 | 
			
		||||
    scene.Initial(virtualDom);
 | 
			
		||||
    await scene.ChangeTextureAsync(options.textureSrc);
 | 
			
		||||
    await scene.ChangeTextureFromUrlAsync(options.textureSrc);
 | 
			
		||||
    var json = btoa(String.fromCharCode(...await DeflateAsync(await scene.SerializeMaterialAsync())));
 | 
			
		||||
    
 | 
			
		||||
    scene.Dispose();
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,7 @@ import type { MaterialRequest } from "../components/MaterialAdjuster.vue"
 | 
			
		||||
 | 
			
		||||
let _libOutputConfig = {
 | 
			
		||||
    textureSrc: "",
 | 
			
		||||
    file: undefined,
 | 
			
		||||
    submitCallback: undefined,
 | 
			
		||||
    cancelCallback: undefined,
 | 
			
		||||
    envTextureSrc: ['./right.webp', './left.webp', './top.webp', './bottom.webp', './front.webp', './back.webp'],
 | 
			
		||||
@@ -19,6 +20,8 @@ export function ConfigureLibOutput(config: Partial<LibOutputConfig>) {
 | 
			
		||||
export type LibOutputConfig = {
 | 
			
		||||
    /** 材质贴图链接 */
 | 
			
		||||
    textureSrc: string,
 | 
			
		||||
    /** 材质预设数据,base64编码 */
 | 
			
		||||
    file?: string,
 | 
			
		||||
    /** 环境贴图链接(立方体贴图,按照顺序输入[右左上下前后]) */
 | 
			
		||||
    envTextureSrc: string[],
 | 
			
		||||
    /** 灰度环境贴图链接,输入格式与环境贴图一致 */
 | 
			
		||||
 
 | 
			
		||||
@@ -19,4 +19,4 @@ export const useEvent = defineStore('event', () => {
 | 
			
		||||
    return { Subscribe, Publish, Unsubscribe };
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export type EventNames = 'submit' | 'update-texture';
 | 
			
		||||
export type EventNames = 'submit' | 'update-texture' | 'update-config';
 | 
			
		||||
@@ -5,7 +5,7 @@ import { Database, ObjectId, PhysicalMaterialRecord, TextureTableRecord } from "
 | 
			
		||||
import { LoadImageFromUrl } from "../helpers/helper.imageLoader";
 | 
			
		||||
import { Texture } from "three";
 | 
			
		||||
import { materialRenderer } from "../common/MaterialRenderer";
 | 
			
		||||
import { MaterialOut } from "../common/MaterialSerializer";
 | 
			
		||||
import { MaterialIn, MaterialOut } from "../common/MaterialSerializer";
 | 
			
		||||
 | 
			
		||||
const sceneSetup = () => {
 | 
			
		||||
    let _editor: MaterialEditor | undefined;
 | 
			
		||||
@@ -77,7 +77,7 @@ const sceneSetup = () => {
 | 
			
		||||
        Update();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async function ChangeTextureAsync(url: string) {
 | 
			
		||||
    async function ChangeTextureFromUrlAsync(url: string) {
 | 
			
		||||
        const img = await LoadImageFromUrl(url);
 | 
			
		||||
        
 | 
			
		||||
        // 关联贴图
 | 
			
		||||
@@ -97,25 +97,38 @@ const sceneSetup = () => {
 | 
			
		||||
        await UpdateMaterialAsync();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function UpdateTexture(adjustment: TextureAdjustment) {
 | 
			
		||||
        const texture = _currTexture.value;
 | 
			
		||||
        texture.wrapS = adjustment.wrapS;
 | 
			
		||||
        texture.wrapT = adjustment.wrapT;
 | 
			
		||||
        texture.anisotropy = 16;
 | 
			
		||||
        texture.rotation = adjustment.rotation;
 | 
			
		||||
        texture.repeat.set(adjustment.repeatX, adjustment.repeatY);
 | 
			
		||||
        texture.offset.set(adjustment.moveX, adjustment.moveY);
 | 
			
		||||
        texture.needsUpdate = true;
 | 
			
		||||
 | 
			
		||||
        Update();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async function SerializeMaterialAsync() {
 | 
			
		||||
        const matJson = MaterialOut(Material.value as PhysicalMaterialRecord);
 | 
			
		||||
        console.log(matJson);
 | 
			
		||||
        return matJson;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async function ApplyTextureAsync(textureRecord: TextureTableRecord)
 | 
			
		||||
    {
 | 
			
		||||
        if (!textureRecord.imageUrl) {
 | 
			
		||||
            alert("该纹理无效");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 绑定纹理
 | 
			
		||||
        let newTexture = textureRecord.Clone() as TextureTableRecord;
 | 
			
		||||
        newTexture.Owner = undefined;
 | 
			
		||||
        newTexture.Name = _database.TextureTable.AllocateName();
 | 
			
		||||
        _database.TextureTable.Add(newTexture);
 | 
			
		||||
 | 
			
		||||
        // 替换map
 | 
			
		||||
        Material.value.map = newTexture.Id;
 | 
			
		||||
 | 
			
		||||
        await UpdateMaterialAsync();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async function ImportMaterialAsync(materialJson: string) {
 | 
			
		||||
        const material = MaterialIn(JSON.parse(materialJson));
 | 
			
		||||
        Material.value = material;
 | 
			
		||||
        _editor.setMaterial(material);
 | 
			
		||||
        await UpdateMaterialAsync();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async function GenerateMaterialLogoAsync() {
 | 
			
		||||
        const blob = await materialRenderer.getBlob(Material.value.Material);
 | 
			
		||||
        return blob;
 | 
			
		||||
@@ -135,14 +148,15 @@ const sceneSetup = () => {
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
        CurrGeometry,
 | 
			
		||||
        CurrTexture,
 | 
			
		||||
        Geometries,
 | 
			
		||||
        Material,
 | 
			
		||||
        Initial,
 | 
			
		||||
        Update,
 | 
			
		||||
        UpdateMaterialAsync,
 | 
			
		||||
        ChangeTextureAsync,
 | 
			
		||||
        UpdateTexture,
 | 
			
		||||
        ChangeTextureFromUrlAsync,
 | 
			
		||||
        SerializeMaterialAsync,
 | 
			
		||||
        ImportMaterialAsync,
 | 
			
		||||
        GenerateMaterialLogoAsync,
 | 
			
		||||
        Dispose
 | 
			
		||||
    };
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user