添加材质切换,调节功能

This commit is contained in:
2025-04-14 16:37:17 +08:00
parent 0855f15a5c
commit 0929d5b80c
35 changed files with 1144 additions and 372 deletions

42
src/components/CfFlex.vue Normal file
View File

@@ -0,0 +1,42 @@
<template>
<div class="cf-flex" :style="{
alignItems: props.align,
justifyContent: props.justify,
flexDirection: flexDirection,
flexWrap: props.wrap ? 'wrap' : 'nowrap',
gap: props.gap
}">
<slot></slot>
</div>
</template>
<script setup lang='ts'>
import { type Property } from 'csstype';
import { computed } from 'vue';
const props = withDefaults(defineProps<{
align?: Property.AlignItems;
justify?: Property.JustifyContent;
vertical?: boolean;
wrap?: boolean;
gap?: string;
reverse?: boolean;
}>(), {
justify: 'normal',
vertical: false,
wrap: true
});
const flexDirection = computed(() =>
props.vertical ?
props.reverse ? 'column-reverse' : 'column'
: props.reverse ? 'row-reverse' : 'row')
</script>
<style scoped>
.cf-flex
{
display: flex;
}
</style>

View File

@@ -0,0 +1,293 @@
<template>
<div vertical class="material-adjuster">
<div class="adjust-section">
<h3>模型预览</h3>
<label>选择模型样式</label>
<select v-model="CurrGeometry">
<option v-for="name in Geometries" :value="name">{{ name }}</option>
</select>
</div>
<div class="adjust-section" v-if="debugMode">
<h3>纹理选择DEBUG</h3>
<label>纹理链接</label>
<input v-model.trim="textureLink" type="text" placeholder="在此键入纹理图像的URL..." />
<button class="btn-primary" @click="scene.ChangeTextureAsync(textureLink)"
style="margin-left: 1em;">应用</button>
</div>
<div class="adjust-section">
<h3>材质调节</h3>
<label>金属度</label>
<CfFlex gap="1em">
<input v-model="model.metallic" type="range" min="0" max="1" step="0.01" />
<span>{{ model.metallic }}</span>
</CfFlex>
<label>粗糙度</label>
<CfFlex gap="1em">
<input v-model="model.roughness" type="range" min="0" max="1" step="0.01" />
<span>{{ model.roughness }}</span>
</CfFlex>
<label>法线强度</label>
<CfFlex gap="1em">
<input v-model="model.normalScale" type="range" min="0" max="1" step="0.01" />
<span>{{ model.normalScale }}</span>
</CfFlex>
<label>高光</label>
<CfFlex gap="1em">
<input v-model="model.emissiveIntensity" type="range" min="0" max="1" step="0.01" />
<span>{{ model.emissiveIntensity }}</span>
</CfFlex>
</div>
<div class="adjust-section">
<h3>纹理调节</h3>
<label>启用纹理</label>
<input type="checkbox" v-model="enableTexture" />
<label>平铺U</label>
<select v-model="textureAdjustment.wrapS">
<option value="0">镜像平铺</option>
<option value="1">平铺</option>
<option value="2">展开</option>
</select>
<label>平铺V</label>
<select v-model="textureAdjustment.wrapT">
<option value="0">镜像平铺</option>
<option value="1">平铺</option>
<option value="2">展开</option>
</select>
<label>旋转</label>
<input v-model="textureAdjustment.rotation" type="number" step="0.01" min="0" max="359" />
<div>
<label>偏移</label>
<span style="margin-right: 0.5em;">X</span>
<input v-model="textureAdjustment.moveX" type="number" step="0.01" style="max-width: 60px;" />
&NonBreakingSpace;
&NonBreakingSpace;
<span style="margin-right: 0.5em;">Y</span>
<input v-model="textureAdjustment.moveY" type="number" step="0.01" style="max-width: 60px;" />
</div>
<div>
<label>尺寸</label>
<span style="margin-right: 0.5em;"></span>
<input v-model="textureAdjustment.repeatX" type="number" step="0.01" style="max-width: 60px;" />
&NonBreakingSpace;
&NonBreakingSpace;
<span style="margin-right: 0.5em;"></span>
<input v-model="textureAdjustment.repeatY" type="number" step="0.01" style="max-width: 60px;" />
</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>
</template>
<script setup lang='ts'>
import { ref, reactive, watch } from "vue"
import { useScene, type TextureAdjustment } from "../stores/sceneStore";
import { storeToRefs } from "pinia";
import CfFlex from "./CfFlex.vue";
import { DirectoryId } from "../api/Request";
import { IsNullOrWhitespace } from "../helpers/helper.string";
const scene = useScene();
const debugMode = ref(true);
const { CurrGeometry, Geometries, Material } = storeToRefs(scene);
const enableTexture = ref(Material.value.useMap);
const textureLink = ref('');
const textureAdjustment = reactive<TextureAdjustment>({
wrapS: 0,
wrapT: 0,
rotation: 0,
repeatX: 1,
repeatY: 1,
moveX: 0,
moveY: 0
});
const uploading = ref(false);
const model = reactive({
metallic: Material.value.matalness,
roughness: Material.value.roughness,
normalScale: Material.value.bumpScale,
emissiveIntensity: Material.value.specular
});
const materialRequest = reactive({
dirId: DirectoryId.MaterialDir, // 正常来说是2
materialName: ''
});
watch(model, async (val) => {
await UpdateMaterial();
});
watch(enableTexture, async (val) => {
await EnableTexture(val);
});
watch(textureAdjustment, async (val) => {
UpdateTexture();
});
async function UpdateMaterial() {
Material.value.matalness = model.metallic;
Material.value.roughness = model.roughness;
Material.value.bumpScale = model.normalScale;
Material.value.specular = model.emissiveIntensity;
// Material.value.side = DoubleSide;
await scene.UpdateMaterialAsync();
}
function UpdateTexture() {
scene.UpdateTexture(textureAdjustment);
}
async function EnableTexture(enable: boolean) {
Material.value.useMap = enable;
await scene.UpdateMaterialAsync();
}
async function HandleUpload() {
try {
if (IsNullOrWhitespace(materialRequest.materialName)) {
alert('材质名称不可为空');
return;
}
uploading.value = true;
await scene.UploadMaterialAsync(materialRequest);
uploading.value = false;
alert("上传成功");
} catch (error) {
console.error(error);
alert("上传材质出错,请检查网络连接或联系管理员");
}
}
async function HandleCancel() {
// TODO: 触发取消事件
}
</script>
<style scoped>
.material-adjuster
{
padding: 1rem;
overflow-y: auto;
}
.adjust-section
{
border-radius: 8px;
border: 1px solid #ccc;
padding: 1rem;
margin-bottom: 1rem;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
background-color: white;
}
h3
{
margin: 0 0 0.5rem 0;
}
label
{
display: block;
}
select
{
width: 220px;
padding: 4px;
margin: 0.5rem 0;
}
input[type="range"]
{
width: 220px;
margin: 0.5rem 0;
}
input[type="text"]
{
width: 220px;
padding: 4px;
margin: 0.5rem 0;
}
input[type="number"]
{
width: 220px;
padding: 4px;
margin: 0.5rem 0;
}
.btn-success
{
background-color: #4caf50;
color: white;
padding: 0.5rem 1rem;
border: none;
border-radius: 4px;
cursor: pointer;
}
.btn-success:hover
{
background-color: #3e8e41;
}
.btn-danger
{
background-color: #f44336;
color: white;
padding: 0.5rem 1rem;
border: none;
border-radius: 4px;
cursor: pointer;
}
.btn-danger:hover
{
background-color: #da190b;
}
.btn-primary
{
background-color: #2196f3;
color: white;
padding: 0.5rem 1rem;
border: none;
border-radius: 4px;
cursor: pointer;
}
.btn-primary:hover
{
background-color: #0a7ed2;
}
</style>

View File

@@ -1,41 +1,32 @@
<template>
<div ref="container" style="width: 800px;height: 800px;"></div>
{{ CurGeometryName }}
<div v-for="geo in geometries">
<button @click="changeGeometry(geo[0])">{{ geo[0] }}</button>
</div>
<CfFlex class="material-view">
<div ref="container" style="width: 100%; height: 100%; flex: 3; box-sizing: border-box;" />
<MaterialAdjuster style="flex: 1;overflow-y: auto; width: 100%; height: 100%; box-sizing: border-box;" />
</CfFlex>
</template>
<script setup lang="ts">
import { onMounted, useTemplateRef } from 'vue';
import { MaterialEditor } from '../common/MaterialEditor';
import { PhysicalMaterialRecord } from 'webcad_ue4_api';
import MaterialAdjuster from './MaterialAdjuster.vue';
import { useScene } from '../stores/sceneStore';
import CfFlex from './CfFlex.vue';
const scene = useScene();
const container = useTemplateRef<HTMLElement>('container');
let editor:MaterialEditor = MaterialEditor.GetInstance();
const geometries = editor.Geometrys;
const material = new PhysicalMaterialRecord();
const CurGeometryName = editor.CurGeometryName;
onMounted(() => {
editor.SetViewer(container.value);
editor.setMaterial(material);
scene.Initial(container.value);
});
const view = editor.Viewer;
view.OnSize(800, 800);
view.ZoomAll();
view.Zoom(2.1);
})
</script>
function changeGeometry(geoName:string) {
CurGeometryName.value = geoName;
let geo = editor.Geometrys.get(CurGeometryName.value);
if (geo) {
editor.ShowMesh.geometry = geo;
editor.Viewer.UpdateRender();
}
<style scoped>
.material-view
{
width: 100%;
height: 100vh;
box-sizing: border-box;
padding: 0;
margin: 0;
overflow: hidden;
}
</script>
</style>