添加编辑模式

This commit is contained in:
陈梓阳 2025-05-09 19:29:09 +08:00
parent d92cdedc57
commit 37158e7cb1
9 changed files with 105 additions and 26 deletions

View File

@ -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",

View File

@ -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: {}

View File

@ -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;

View File

@ -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">

View 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;
}

View File

@ -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();

View File

@ -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[],
/** 灰度环境贴图链接,输入格式与环境贴图一致 */

View File

@ -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';

View File

@ -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
};