!1622 重构:材质界面

pull/1601/MERGE
黄诗津 3 years ago committed by ChenX
parent b679fb4420
commit bfa8f1196b

@ -310,22 +310,26 @@ export function ConverMaterialData(material: PhysicalMaterialRecord)
d.baseColorDarkColor = material.baseColorDarkColor.toArray();
d.baseColorSaturability = material.baseColorSaturability;
//透明度附加
d.opacityContrast = material.opacityContrast;
if (material.type === "玻璃")//透明度附加
{
d.opacityContrast = (1 - material.opacity) * 3;
d.opacityBorder = material.opacityBorder;
d.opacityMaximum = material.opacityMaximum;
d.opacityMinimum = material.opacityMinimum;
}
//高光
d.specular = material.specular;
d.selfLuminous = material.selfLuminous;//自发光
//菲涅尔
if (material.type === "布料" || material.type === "皮革")//菲涅尔
{
d.fresnelPO = material.fresnelPO;
d.fresnelST = material.fresnelST;
d.fresnelLuminance = material.fresnelLuminance;
d.fresnelLightColor = material.fresnelLightColor.toArray();
d.fresnelDarkColor = material.fresnelDarkColor.toArray();
}
//锐化
d.sharpen = material.sharpen;

@ -236,6 +236,7 @@ export const DefaultParamMap = Object.freeze({
selfLuminous: 0,//自发光强度 范围0-200
//#region 透明度附加 默认折叠
opacity: 0.6,
opacityContrast: 1, //不透明度对比 默认1
opacityBorder: 1, //不透明度边界 默认1
opacityMaximum: 1, //不透明度最大值 默认1
@ -310,21 +311,24 @@ export const DefaultParamMap = Object.freeze({
selfLuminous: 0,//自发光强度 范围0-200
})) as MaterialParam,
// 自定义: Object.freeze(Object.assign({ ...DefaultParam }, {
// })) as MaterialParam,
});
export type MaterialType = keyof (typeof DefaultParamMap);
export function SetMaterialParams(mtl: PhysicalMaterialRecord, param: MaterialParam): void
{
for (let key in DefaultParamMap.)
for (let key in param)
{
let v = DefaultParamMap.[key];;
let v = param[key];
if (v instanceof Color)
{
let c = mtl[key] as Color;
c.copy(v);
}
else
mtl[key] = DefaultParamMap.[key];
mtl[key] = param[key];
}
}

@ -95,8 +95,12 @@ export class PhysicalMaterialRecord extends MaterialTableRecord
this.material.color.set(this.color);
this.material.transparent = this.transparent;
this.material.metalness = this.matalness;
this.material.opacity = this.opacity;
if (this.type === "玻璃")
this.material.metalness = 0.2;
else
this.material.metalness = Math.min(0.8, this.matalness);
this.material.opacity = Math.max(0.1, this.opacity);
this.material.depthTest = this.depthTest;
this.material.bumpScale = this.bumpScale;
this.material.roughness = this.roughness;
@ -110,6 +114,9 @@ export class PhysicalMaterialRecord extends MaterialTableRecord
this.material.map = texture;
this.material.needsUpdate = true;
}
else
this.material.map = undefined;
if (this.useBumpMap && this.bumpMap && !this.bumpMap.IsErase)
{
let map = this.bumpMap.Object as TextureTableRecord;
@ -118,6 +125,9 @@ export class PhysicalMaterialRecord extends MaterialTableRecord
this.material.bumpMap = texture;
this.material.needsUpdate = true;
}
else
this.material.bumpMap = undefined;
if (this.roughnessMap && this.roughnessMap && !this.roughnessMap.IsErase)
{
let map = this.roughnessMap.Object as TextureTableRecord;
@ -126,6 +136,9 @@ export class PhysicalMaterialRecord extends MaterialTableRecord
this.material.roughnessMap = texture;
this.material.needsUpdate = true;
}
else
this.material.roughnessMap = undefined;
this.material.needsUpdate = true;
this.AsyncUpdated();

@ -22,6 +22,8 @@ export class TextureTableRecord extends SymbolTableRecord
@AutoRecord moveX = 0;//材质位移
@AutoRecord moveY = 0;
@AutoRecord imgUrl: string = "";
set WrapS(wrap: Wrapping)
{
if (wrap !== this.wrapS)
@ -46,23 +48,23 @@ export class TextureTableRecord extends SymbolTableRecord
private texture: Texture = new Texture();
async Update()
{
let imgUrl: string;
if (this.imageUrl.endsWith("Default.png") || !this.imageUrl)
{
imgUrl = ResourcesCDN_HOST + "Default.webp";
this.imgUrl = ResourcesCDN_HOST + "Default.webp";
this.imageUrl = "";
}
else
imgUrl = CURRENT_HOST + "/" + this.imageUrl;
this.imgUrl = CURRENT_HOST + "/" + this.imageUrl;
if (!this.texture.image || (this.texture.image && this.texture.image.src !== imgUrl))
this.texture.image = await LoadImageFromUrl(imgUrl);
if (!this.texture.image || (this.texture.image && this.texture.image.src !== this.imgUrl))
this.texture.image = await LoadImageFromUrl(this.imgUrl);
this.texture.wrapS = this.wrapS;
this.texture.wrapT = this.wrapT;
this.texture.anisotropy = 16;
this.texture.rotation = this.rotation;
this.texture.repeat.set(this.repeatX, this.repeatY);
this.texture.offset.set(this.moveX, this.moveY);
this.texture.needsUpdate = true;
for (let f of this.waits)

@ -15,7 +15,7 @@ export class MaterialEditor extends Singleton
{
Geometrys: Map<string, Geometry | BufferGeometry>;
m_CurGeometryName = observable.box("球");
CurGeometryName = observable.box("球");
Canvas: HTMLElement;
ShowObject: Object3D;
ShowMesh: Mesh;
@ -36,8 +36,8 @@ export class MaterialEditor extends Singleton
["立方体", new BoxBufferGeometry(1e3, 1e3, 1e3, 1, 1, 1)],
["环面纽结", new TorusKnotBufferGeometry(0.7 * 1e3, 0.3 * 1e3, 128, 64)],
["圆锥体", new ConeBufferGeometry(1 * 1e3, 2 * 1e3, 32)],
["球(多面)", new SphereBufferGeometry(1 * 1e3, 64, 64)],
["立方体(多面)", new BoxBufferGeometry(1 * 1e3, 1 * 1e3, 1 * 1e3, 128, 128, 128)]
// ["球(多面)", new SphereBufferGeometry(1 * 1e3, 64, 64)],
// ["立方体(多面)", new BoxBufferGeometry(1 * 1e3, 1 * 1e3, 1 * 1e3, 128, 128, 128)]
]
);
}
@ -49,6 +49,7 @@ export class MaterialEditor extends Singleton
this.Viewer.PreViewer.Cursor.CursorObject.visible = false;
this.Viewer.CameraCtrl.CameraType = CameraType.PerspectiveCamera;
this.Viewer.UsePass = false;
this.Viewer.Renderer.setClearColor(0xE0E0E0, 1);
this.initScene();
new MaterialEditorCamerControl(this.Viewer);
}
@ -62,13 +63,13 @@ export class MaterialEditor extends Singleton
{
this.Canvas = canvas;
this.initViewer();
this.m_CurGeometryName.set("球");
this.CurGeometryName.set("球");
}
initScene()
{
let scene = this.Viewer.Scene;
this.ShowObject = new Object3D();
let geo = this.Geometrys.get(this.m_CurGeometryName.get());
let geo = this.Geometrys.get(this.CurGeometryName.get());
this.ShowMesh = new Mesh(geo);
this.ShowMesh.scale.set(1000, 1000, 1000);
@ -76,7 +77,7 @@ export class MaterialEditor extends Singleton
scene.add(this.ShowObject);
let remove = autorun(() =>
{
let geo = this.Geometrys.get(this.m_CurGeometryName.get());
let geo = this.Geometrys.get(this.CurGeometryName.get());
if (geo)
{
this.ShowMesh.geometry = geo;
@ -111,7 +112,7 @@ export class MaterialEditor extends Singleton
async Update()
{
let mat = this.ShowMesh.material as MeshPhysicalMaterial;
if (this.Material.map && this.Material.map.Object)
if (this.Material.map && this.Material.map.Object && this.Material.useMap)
{
let texture = this.Material.map.Object as TextureTableRecord;
await texture.Update();

@ -202,7 +202,7 @@ export class MaterialContainer extends React.Component<MaterialContainerProps, {
id={MATCONTENTID}
style={{
width: 1200,
height: 820
height: 840
}}
>
</div>

@ -5,7 +5,7 @@ import ResizeObserver from 'resize-observer-polyfill';
import { PhysicalMaterialRecord } from '../../DatabaseServices/PhysicalMaterialRecord';
import { MaterialEditor } from '../../Editor/MaterialEditor';
import { MATCONTENTID } from '../Components/MaterialContainer';
import { Select } from '../MaterialEditor/MaterialCommon';
import { Objects } from '../MaterialEditor/MaterialCommon';
import { PropertiesPane } from '../MaterialEditor/PropertiesPane';
import { MaterialStore } from '../Store/MaterialStore';
@ -24,6 +24,7 @@ export class MaterialEditorLayout
private initViewer()
{
let canvas = document.getElementById("MaterialCanvas");
canvas.style.position = "relative";
this.editor = MaterialEditor.GetInstance() as MaterialEditor;
this.editor.SetViewer(canvas);
this.editor.setMaterial(this.material);
@ -54,6 +55,10 @@ export class MaterialEditorLayout
this.htmlEl.appendChild(canvasContainer);
let object = document.createElement("div");
object.id = "object";
canvasContainer.appendChild(object);
let rightContainer = document.createElement("div");
this.htmlEl.appendChild(rightContainer);
@ -68,24 +73,28 @@ export class MaterialEditorLayout
rightContainer.appendChild(obejctContainer);
}
getSceneConfigEl()
{
return document.getElementById("SceneConfig");
}
getAttributePanelEl()
{
return document.getElementById("MaterialAttribute");
}
getobject()
{
return document.getElementById("object");
}
renderSceneConfig()
{
ReactDOM.render(
<Select prompt="物体"
value={this.editor.m_CurGeometryName}
<Objects
value={this.editor.CurGeometryName}
selects={[...this.editor.Geometrys.keys()].map(k =>
{
return { label: k, value: k };
})} />
, this.getSceneConfigEl());
return { lable: k, value: k };
})}
lable={["球", "环", "立", "结", "椎"]}
/>
, this.getobject()
);
}
renderAttributePanel()
@ -101,7 +110,7 @@ export class MaterialEditorLayout
dispose()
{
ReactDOM.unmountComponentAtNode(this.getSceneConfigEl());
ReactDOM.unmountComponentAtNode(this.getobject());
ReactDOM.unmountComponentAtNode(this.getAttributePanelEl());
(MaterialStore.GetInstance() as MaterialStore).Destroy();
this.destroyFuncs.forEach(f => f());

@ -1,9 +1,13 @@
import { autorun, IObservableValue, observable } from "mobx";
import { Icon, InputGroup, Popover, PopoverPosition, Tooltip } from "@blueprintjs/core";
import { action, autorun, IObservableValue, observable } from "mobx";
import { observer } from "mobx-react";
import * as React from "react";
import { SketchPicker } from 'react-color';
import { ClampToEdgeWrapping, MirroredRepeatWrapping, RepeatWrapping } from "three";
import { ClampToEdgeWrapping, Color, MirroredRepeatWrapping, RepeatWrapping } from "three";
import { safeEval } from "../../Common/eval";
import { DefaultParamMap, MaterialType, SetMaterialParams } from "../../DatabaseServices/IMaterialDefaultParam";
import { PhysicalMaterialRecord } from "../../DatabaseServices/PhysicalMaterialRecord";
import { MaterialStore } from "../Store/MaterialStore";
const LabelStyle: React.CSSProperties = { display: "flex", margin: 3 };
const PromptStyle: React.CSSProperties = { alignSelf: "center", width: 60 };
@ -13,6 +17,21 @@ const InputStyle: React.CSSProperties = {
width: "100%",
overflow: 'hidden'
};
const TypeSelectStyle: React.CSSProperties = {
position: "absolute",
right: 0,
bottom: 8,
display: "flex"
};
const TypeSelectButton: React.CSSProperties = {
width: 30,
height: 30,
backgroundColor: "white",
borderRadius: "8px",
lineHeight: "25px",
textAlign: "center",
overflow: "hidden"
};
export class Input extends React.Component<
{
@ -106,27 +125,73 @@ export class ColorSelect extends React.Component<{ color: IObservableValue<strin
render()
{
return (
<>
<button
style={{ width: 100, height: 25, background: this.props.color.get() }}
onClick={e =>
{
this.bSelectColor = true;
}} />
{this.bSelectColor &&
<div style={PopoverStyle}>
<div
style={CoverStyle}
onClick={this.handleClose} />
<Popover
position={PopoverPosition.TOP}
minimal
content={
<SketchPicker
color={this.props.color.get()}
onChange={color =>
onChange={(color) =>
{
this.props.color.set(color.hex);
}}
/>
</div>}
</>
}
>
<InputGroup
style={{ height: 24, width: 90, marginTop: 2 }}
value={this.props.color.get()}
small
leftElement={
<Icon
icon='stop'
iconSize={20}
color={this.props.color.get()}
/>
}
/>
</Popover>
);
}
}
@observer
export class LightDarkColor extends React.Component<{ color: IObservableValue<Color>; }, {}>{
@observable bSelectColor = false;
handleClose = (e: React.MouseEvent<HTMLDivElement, MouseEvent>) =>
{
this.bSelectColor = false;
e.preventDefault();
e.stopPropagation();
};
render()
{
return (
<Popover
position={PopoverPosition.TOP}
minimal
content={
<SketchPicker
color={this.props.color.get()}
onChange={(color) =>
{
this.props.color.set(new Color(color.hex));
}}
/>
}
>
<InputGroup
style={{ height: 24, width: 90, marginTop: 2 }}
value={`#${this.props.color.get().getHexString()}`}
small
leftElement={
<Icon
icon='stop'
iconSize={20}
color={`#${this.props.color.get().getHexString()}`}
/>
}
/>
</Popover>
);
}
}
@ -303,6 +368,120 @@ export class Select extends React.Component<{ value: IObservableValue<any>, prom
}
}
export class Objects extends React.Component<{ value: IObservableValue<any>, selects: { lable: string, value: any; }[], lable: string[]; }, {}>
{
render()
{
return (
<div style={{ ...TypeSelectStyle }} >
{this.props.selects.map((op, i) =>
<Tooltip content={op.lable}>
<button type="button" key={i}
style={{ ...TypeSelectButton }}
onClick={() => { this.props.value.set(op.value); }}
>
{this.props.lable[i]}
</button>
</Tooltip>
)}
</div>
);
}
}
export class TypeSelect extends React.Component<{ value: IObservableValue<MaterialType>, prompt: string, selects: MaterialType[], store: MaterialStore; }, {}>{
selectEl: HTMLSelectElement;
dispose: Function;
componentDidMount()
{
this.dispose = autorun(() =>
{
let value = this.props.value.get();
if (this.selectEl)
this.selectEl.value = value;
});
}
componentWillUnmount()
{
if (this.dispose)
this.dispose();
}
setMaterial = action((material: PhysicalMaterialRecord) =>
{
// if (material.type === "自定义")
// return;
let Material = material;
let store = this.props.store;
store.matalness.set(Material.matalness);
store.roughness.set(Material.roughness);
store.specular.set(Material.specular);
store.bumpScale.set(Material.bumpScale);
store.baseColorLuminance.set(Material.baseColorLuminance);
store.baseColorSaturability.set(Material.baseColorSaturability);
store.baseColorLightColor.set(Material.baseColorLightColor);
store.baseColorDarkColor.set(Material.baseColorDarkColor);
store.useTransparent.set(material.transparent);
if (material.type === "布料" || material.type === "皮革")
{
store.fresnelPO.set(Material.fresnelPO);
store.fresnelST.set(Material.fresnelST);
store.fresnelLuminance.set(Material.fresnelLuminance);
store.fresnelLightColor.set(Material.fresnelLightColor);
store.fresnelDarkColor.set(Material.fresnelDarkColor);
}
if (material.type === "玻璃")
{
store.transparent.set(1 - Material.opacity);
store.opacityContrast.set(Material.opacityContrast);
store.opacityBorder.set(Material.opacityBorder);
store.opacityMaximum.set(Material.opacityMaximum);
store.opacityMinimum.set(Material.opacityMinimum);
}
});
render()
{
return (
<label style={LabelStyle}>
<span style={{ ...PromptStyle, ...{ width: 50 } }}>
{this.props.prompt}
</span>
<select
style={{ flexGrow: 1 }}
defaultValue={this.props.value.get()}
ref={e => this.selectEl = e}
onChange={action(e =>
{
let PhysicalMaterial = new PhysicalMaterialRecord();
for (let op of this.props.selects)
{
if (op === e.target.value)
{
this.props.value.set(op);
SetMaterialParams(PhysicalMaterial, DefaultParamMap[op]);
PhysicalMaterial.type = op;
if (op === "玻璃")
PhysicalMaterial.transparent = true;
else
PhysicalMaterial.transparent = false;
this.setMaterial(PhysicalMaterial);
return;
}
}
})}
>
{
this.props.selects.map((op, i) => <option key={i} value={op}>{op}</option>)
}
</select>
</label >
);
}
}
export const WrapSelects = [
{ label: "镜像平铺", value: MirroredRepeatWrapping },

@ -1,37 +1,113 @@
import { Divider, Checkbox } from "@blueprintjs/core";
import { Button, Checkbox, Collapse, Divider, Radio, RadioGroup } from "@blueprintjs/core";
import { inject, observer } from "mobx-react";
import * as React from "react";
import { DefaultParamMap } from "../../DatabaseServices/IMaterialDefaultParam";
import { MaterialStore } from "../Store/MaterialStore";
import { Check, ColorSelect, Lable, LableInput, Slider, Input } from "./MaterialCommon";
import { Check, ColorSelect, Lable, LableInput, LightDarkColor, Slider, TypeSelect } from "./MaterialCommon";
import { MaterialLinkShopId } from "./MaterialLinkShop";
import { Texture } from "./TextureItem";
import { TextureList } from "./TextureList";
const ButtonStyle: React.CSSProperties = {
fontSize: 14, outline: "none", justifyContent: "left"
};
//材质的属性面板
@inject('store')
@observer
export class PropertiesPane extends React.Component<{ store?: MaterialStore; }, {}>{
public state = {
materialExtension: true,
colorExtension: false,
texturaExtension: true,
fresnelExtension: true,
OpacityExtension: true,
};
render()
{
let store = this.props.store;
let ParamMap = [];
for (const key in DefaultParamMap)
{
ParamMap.push(key);
}
return (
<>
<div style={{
fontSize: "14px",
margin: "5px"
marginLeft: "5px",
width: 395,
height: 840,
overflow: "scroll"
}}>
<LableInput prompt="名称" value={store.name} />
<Divider />
<Button
minimal
text="材质"
icon={this.state.materialExtension ? "caret-down" : "caret-right"}
style={ButtonStyle}
onClick={() => { this.setState({ materialExtension: !this.state.materialExtension }); }}
/>
{
this.state.materialExtension &&
<MaterialLinkShopId />
<Lable prompt="颜色" style={{ width: "50%" }}>
}
<Divider />
<TypeSelect prompt="类型"
value={store.materialType}
selects={[...ParamMap]}
store={store} />
<Divider />
<div style={{ display: "flex" }}>
<Button
minimal
text="颜色"
icon={this.state.colorExtension ? "caret-down" : "caret-right"}
style={ButtonStyle}
onClick={() => { this.setState({ colorExtension: !this.state.colorExtension }); }}
/>
<ColorSelect color={store.color} />
</div>
{
this.state.colorExtension &&
<div>
<Slider prompt="亮度" value={store.baseColorLuminance} min={-1} max={1} step={0.1} />
{/* <Lable prompt="" style={{ width: "50%" }}>
<LightDarkColor color={store.baseColorLightColor} />
</Lable> */}
<Lable prompt="暗部颜色" style={{ width: "50%" }}>
<LightDarkColor color={store.baseColorDarkColor} />
</Lable>
<Check prompt="透明" value={store.useTransparent} />
<Slider prompt="透明度" value={store.transparent} min={0} max={1} step={0.1} />
<Slider prompt="凹凸度" value={store.bumpScale} min={0} max={1} step={0.1} />
<Slider prompt="饱和度" value={store.baseColorSaturability} min={0} max={1} step={0.1} />
</div>
}
<Divider />
<Button
minimal
text="质感"
icon={this.state.texturaExtension ? "caret-down" : "caret-right"}
style={ButtonStyle}
onClick={() => { this.setState({ texturaExtension: !this.state.texturaExtension }); }}
/>
<Collapse isOpen={this.state.texturaExtension} keepChildrenMounted={true}>
<Slider prompt="法线强度" value={store.bumpScale} min={0} max={1} step={0.1} />
<Slider prompt="粗糙度" value={store.roughness} min={0} max={1} step={0.1} />
<Slider prompt="金属度" value={store.matalness} min={0} max={1} step={0.1} />
<Checkbox label="铺满到板件" checked={store.isFull} onChange={() => store.isFull = !store.isFull} />
<Slider prompt="高光" value={store.specular} min={0} max={1} step={0.1} />
</Collapse>
<Divider />
<div style={{ display: "flex" }}>
<Checkbox style={{ paddingRight: 50 }} label="铺满到板件" checked={store.isFull} onChange={() => store.isFull = !store.isFull} />
<RadioGroup
inline
selectedValue={this.props.store.coordinate}
onChange={(e) => { this.props.store.coordinate = e.currentTarget.value; }}>
<Radio label="世界坐标" value="0" />
<Radio label="UV坐标" value="1" />
</RadioGroup>
</div>
<div style={{
overflowY: "auto",
overflowX: "hidden",
@ -39,18 +115,70 @@ export class PropertiesPane extends React.Component<{ store?: MaterialStore; },
<Texture
prompt="纹理贴图"
textureStore={store.textureMaping}
isopen={true}
/>
<Divider />
<Texture
{/* <Texture
prompt="凹凸贴图"
textureStore={store.bumpMaping}
isopen={false}
/>
<Divider />
<Texture
prompt="粗糙贴图"
textureStore={store.roughnessMaping}
isopen={false}
/> */}
</div>
{
(store.materialType.get() === "皮革" || store.materialType.get() === "布料" /*|| store.materialType.get() === "自定义"*/) &&
<div>
<Button
minimal
text="菲涅尔"
icon={this.state.fresnelExtension ? "caret-down" : "caret-right"}
style={ButtonStyle}
onClick={() => { this.setState({ fresnelExtension: !this.state.fresnelExtension }); }}
/>
{
this.state.fresnelExtension &&
<>
<Slider prompt="对比度" value={store.fresnelPO} min={-1} max={10} step={0.1} />
<Slider prompt="强度" value={store.fresnelST} min={0} max={20} step={0.1} />
<Slider prompt="亮度" value={store.fresnelLuminance} min={0} max={20} step={0.1} />
{/* <Lable prompt="" style={{ width: "50%" }}>
<LightDarkColor color={store.fresnelLightColor} />
</Lable> */}
<Lable prompt="暗部颜色" style={{ width: "50%" }}>
<LightDarkColor color={store.fresnelDarkColor} />
</Lable>
</>
}
</div>
}
{
(store.materialType.get() === "玻璃" /* || store.materialType.get() === "自定义"*/) &&
<div>
<Button
minimal
text="不透明度"
icon={this.state.OpacityExtension ? "caret-down" : "caret-right"}
style={ButtonStyle}
onClick={() => { this.setState({ OpacityExtension: !this.state.OpacityExtension }); }}
/>
{
this.state.OpacityExtension &&
<>
<Check prompt="透明" value={store.useTransparent} />
<Slider prompt="透明度" value={store.transparent} min={0} max={1} step={0.1} />
{/* <Slider prompt="对比" value={store.opacityContrast} min={0} max={1} step={0.1} /> */}
<Slider prompt="边界" value={store.opacityBorder} min={0} max={1} step={0.1} />
<Slider prompt="最大值" value={store.opacityMinimum} min={0} max={1} step={0.1} />
<Slider prompt="最小值" value={store.opacityMaximum} min={0} max={1} step={0.1} />
</>
}
</div>
}
</div >
<TextureList />
</>

@ -1,4 +1,4 @@
import { Button, Intent, Label } from "@blueprintjs/core";
import { Button, Collapse, Icon, Intent, Label } from "@blueprintjs/core";
import { inject, observer } from "mobx-react";
import * as React from "react";
import { ClampToEdgeWrapping, MirroredRepeatWrapping, RepeatWrapping } from "three";
@ -6,16 +6,22 @@ import { TextureTableRecord } from "../../DatabaseServices/Texture";
import { MaterialStore } from "../Store/MaterialStore";
import { TextureStore } from "../Store/TextureStore";
import { Check, Input, Select } from "./MaterialCommon";
import { CURRENT_HOST } from "../../Common/HostUrl";
const PromptStyle: React.CSSProperties = { alignSelf: "center", width: 60 };
const SpanStyle: React.CSSProperties = { alignSelf: "center", width: 20 };
const SpanStyle: React.CSSProperties = { alignSelf: "center", textAlign: "center", width: 20 };
const InputStyle: React.CSSProperties = {
flexBasis: "0%",
flexGrow: 1,
width: "100%",
overflow: 'hidden'
};
const ImageStyle: React.CSSProperties = {
width: "150%",
height: "150%",
position: "absolute",
left: "-25%",
top: " -25%"
};
export const WrapSelects = [
{ label: "镜像平铺", value: MirroredRepeatWrapping },
{ label: "平铺", value: RepeatWrapping },
@ -26,11 +32,20 @@ interface ITextureProps
prompt: string;
textureStore: TextureStore;
store?: MaterialStore;
isopen?: boolean;
}
@inject('store')
@observer
export class Texture extends React.Component<ITextureProps, {}>{
export class Texture extends React.Component<ITextureProps, { isopen: boolean; }, {}>{
constructor(props)
{
super(props);
this.state = {
isopen: props.isopen
};
}
handleSelectImg = () =>
{
const { store, textureStore } = this.props;
@ -54,7 +69,32 @@ export class Texture extends React.Component<ITextureProps, {}>{
<>
<div>
<div className="flex-between">
<span style={{ fontSize: 16 }}>{this.props.prompt}:</span>
<Button
minimal
text={`${this.props.prompt}:`}
icon={this.state.isopen ? "caret-down" : "caret-right"}
style={{ fontSize: 14, width: "60%", outline: "none", justifyContent: "left" }}
onClick={() => { this.setState({ isopen: !this.state.isopen }); }}
/>
{
!this.state.isopen &&
<div
style={{
position: "relative",
userSelect: "none",
overflow: "hidden",
width: 30,
height: 30
}}>
<img
src={textureStore.imgUrl ? textureStore.imgUrl : undefined}
style={{
...ImageStyle,
transform: `rotateZ(${textureStore.rotation}deg)`
}}
/>
</div>
}
<Button
intent={Intent.PRIMARY}
style={{
@ -62,6 +102,7 @@ export class Texture extends React.Component<ITextureProps, {}>{
height: 20,
lineHeight: "20px",
padding: "0 10px",
marginTop: 5,
}}
text="复用纹理"
onClick={() =>
@ -71,21 +112,29 @@ export class Texture extends React.Component<ITextureProps, {}>{
}}
/>
</div>
<Collapse isOpen={this.state.isopen} keepChildrenMounted={true}>
<div style={{ display: 'flex' }}>
<div className="img-select-hint" onDoubleClick={this.handleSelectImg}>
<div className="img-select-hint" onDoubleClick={this.handleSelectImg} style={{ width: 140, height: 140 }}>
{
!textureStore.use.get() &&
<div
style={{ color: "#eee", position: "absolute", top: 0, right: 0, zIndex: 2 }}>
<Icon icon="disable" />
使
</div>
}
<img
src={textureStore.textureImg ? CURRENT_HOST + "/" + textureStore.textureImg : undefined}
src={textureStore.imgUrl ? textureStore.imgUrl : undefined}
style={{
width: 140,
height: 140,
...ImageStyle,
transform: `rotateZ(${textureStore.rotation}deg)`
}}
/>
</div>
<div style={{ flexBasis: 0, flexGrow: 1 }}>
<Check prompt="使用" value={textureStore.use} promptStyle={{ width: 50 }} />
<Select prompt="包装U" value={textureStore.warpS} selects={WrapSelects} />
<Select prompt="包装V" value={textureStore.wrapT} selects={WrapSelects} />
<Select prompt="平铺U" value={textureStore.warpS} selects={WrapSelects} />
<Select prompt="平铺V" value={textureStore.wrapT} selects={WrapSelects} />
<Label style={{
display: "flex",
margin: 3,
@ -97,6 +146,19 @@ export class Texture extends React.Component<ITextureProps, {}>{
value={textureStore.rotation}
/>
</Label>
<div style={{ margin: 3, display: "flex" }}>
<span style={{ ...PromptStyle, width: 50, whiteSpace: "nowrap" }}></span>
<span style={SpanStyle}>X</span>
<Input
value={textureStore.moveX}
disabled={textureStore.warpS.get() === ClampToEdgeWrapping}
/>
<span style={SpanStyle}>Y</span>
<Input
value={textureStore.moveY}
disabled={textureStore.wrapT.get() === ClampToEdgeWrapping}
/>
</div>
<div style={{ margin: 3, display: "flex" }}>
<span style={{ ...PromptStyle, width: 50, whiteSpace: "nowrap" }}>(m)</span>
<span style={SpanStyle}></span>
@ -110,10 +172,12 @@ export class Texture extends React.Component<ITextureProps, {}>{
disabled={textureStore.wrapT.get() === ClampToEdgeWrapping}
/>
</div>
</div>
</div>
</Collapse>
</div>
</>
);
}
}
};

@ -1,10 +1,12 @@
import { Intent } from "@blueprintjs/core";
import { action, autorun, observable, toJS } from "mobx";
import { Color } from "three";
import { end } from "xaop";
import { ConverMaterialData } from "../../Add-on/ExportData";
import { app } from "../../ApplicationServices/Application";
import { Singleton } from "../../Common/Singleton";
import { UpdateDraw } from "../../Common/Status";
import { MaterialType } from "../../DatabaseServices/IMaterialDefaultParam";
import { IGoodProps, PhysicalMaterialRecord } from "../../DatabaseServices/PhysicalMaterialRecord";
import { TextureTableRecord } from "../../DatabaseServices/Texture";
import { MaterialEditor } from "../../Editor/MaterialEditor";
@ -23,6 +25,13 @@ export class MaterialStore extends Singleton
matalness = observable.box(0.2);
// 基础色
baseColorLuminance = observable.box(0);
baseColorLightColor = observable.box(new Color(0.5, 0.5, 0.5));
baseColorDarkColor = observable.box(new Color(0, 0, 0));
baseColorSaturability = observable.box(1);
materialType = observable.box<MaterialType>("木纹");
useTransparent = observable.box(false);
transparent = observable.box(0);
@ -30,8 +39,24 @@ export class MaterialStore extends Singleton
bumpScale = observable.box(0);
bumpMaping = new TextureStore();
roughness = observable.box(0);
specular = observable.box(0);
roughnessMaping = new TextureStore();
// 菲涅尔
fresnelPO = observable.box(1);
fresnelST = observable.box(1);
fresnelLuminance = observable.box(1);
fresnelLightColor = observable.box(new Color(1, 1, 1));
fresnelDarkColor = observable.box(new Color(1, 1, 1));
// 不透明度
opacityContrast = observable.box(1);
opacityBorder = observable.box(1);
opacityMaximum = observable.box(1);
opacityMinimum = observable.box(0.3);
protected reactionDestroy;
@observable coordinate = "0";
@observable isOpenTexture = false;
@observable isOpenImgList = false;
@observable goodsInfo: IGoodProps = {
@ -75,10 +100,25 @@ export class MaterialStore extends Singleton
{
this.name.get();
this.color.get();
this.baseColorLuminance.get();
this.baseColorLightColor.get();
this.baseColorDarkColor.get();
this.baseColorSaturability.get();
this.materialType.get();
this.useTransparent.get();
this.transparent.get();
this.bumpScale.get();
this.roughness.get();
this.specular.get();
this.fresnelPO.get();
this.fresnelST.get();
this.fresnelLuminance.get();
this.fresnelLightColor.get();
this.fresnelDarkColor.get();
this.opacityContrast.get();
this.opacityBorder.get();
this.opacityMaximum.get();
this.opacityMinimum.get();
if (this.Material)
this.UpdateMaterial();
});
@ -136,13 +176,6 @@ export class MaterialStore extends Singleton
this.Material.map = this.textureMaping.textureId;
this.Material.bumpMap = this.bumpMaping.textureId;
this.Material.roughnessMap = this.roughnessMaping.textureId;
//同步到效果图
if (userConfig.synchronousEnable)
{
let d: IncrementData = { Add: { Entitys: [], Materials: [] }, Change: { Entitys: [], Materials: [ConverMaterialData(this.Material)] }, Delete: { Entitys: [], Materials: [] } };
app.WebSocket.Send(JSON.stringify(d));
}
}
UpdateMaterial = async () =>
{
@ -150,11 +183,26 @@ export class MaterialStore extends Singleton
{
this.Material.Name = this.name.get();
this.Material.color = this.color.get();
this.Material.baseColorLuminance = this.baseColorLuminance.get();
this.Material.baseColorLightColor = this.baseColorLightColor.get();
this.Material.baseColorDarkColor = this.baseColorDarkColor.get();
this.Material.baseColorSaturability = this.baseColorSaturability.get();
this.Material.type = this.materialType.get() as MaterialType;
this.Material.transparent = this.useTransparent.get();
this.Material.matalness = this.matalness.get();
this.Material.opacity = 1 - this.transparent.get();
this.Material.bumpScale = this.bumpScale.get();
this.Material.roughness = this.roughness.get();
this.Material.specular = this.specular.get();
this.Material.fresnelPO = this.fresnelPO.get();
this.Material.fresnelST = this.fresnelST.get();
this.Material.fresnelLuminance = this.fresnelLuminance.get();
this.Material.fresnelLightColor = this.fresnelLightColor.get();
this.Material.fresnelDarkColor = this.fresnelDarkColor.get();
this.Material.opacityContrast = this.opacityContrast.get();
this.Material.opacityBorder = this.opacityBorder.get();
this.Material.opacityMaximum = this.opacityMaximum.get();
this.Material.opacityMinimum = this.opacityMinimum.get();
this.Material.useMap = this.textureMaping.use.get();
this.Material.useBumpMap = this.bumpMaping.use.get();
this.Material.useRoughnessMap = this.roughnessMaping.use.get();
@ -168,6 +216,13 @@ export class MaterialStore extends Singleton
await this.Material.Update();
await (MaterialEditor.GetInstance() as MaterialEditor).Update();
//同步到效果图 (3个贴图的Store都会触发这个 一共触发了4次)
if (userConfig.synchronousEnable)
{
let d: IncrementData = { Add: { Entitys: [], Materials: [] }, Change: { Entitys: [], Materials: [ConverMaterialData(this.Material)] }, Delete: { Entitys: [], Materials: [] } };
app.WebSocket.Send(JSON.stringify(d));
}
}
};
@action
@ -177,11 +232,26 @@ export class MaterialStore extends Singleton
{
this.name.set(this.Material.Name);
this.color.set(this.Material.color);
this.baseColorLuminance.set(this.Material.baseColorLuminance);
this.baseColorLightColor.set(this.Material.baseColorLightColor);
this.baseColorDarkColor.set(this.Material.baseColorDarkColor);
this.baseColorSaturability.set(this.Material.baseColorSaturability);
this.materialType.set(this.Material.type);
this.useTransparent.set(this.Material.transparent);
this.matalness.set(this.Material.matalness);
this.transparent.set(parseFloat((1 - this.Material.opacity).toFixed(2)));
this.bumpScale.set(this.Material.bumpScale);
this.roughness.set(this.Material.roughness);
this.specular.set(this.Material.specular);
this.fresnelPO.set(this.Material.fresnelPO);
this.fresnelST.set(this.Material.fresnelST);
this.fresnelLuminance.set(this.Material.fresnelLuminance);
this.fresnelLightColor.set(this.Material.fresnelLightColor);
this.fresnelDarkColor.set(this.Material.fresnelDarkColor);
this.opacityContrast.set(this.Material.opacityContrast);
this.opacityBorder.set(this.Material.opacityBorder);
this.opacityMaximum.set(this.Material.opacityMaximum);
this.opacityMinimum.set(this.Material.opacityMinimum);
this.isFull = this.Material.IsFull;
Object.assign(this.goodsInfo, this.Material.GoodsInfo);

@ -1,4 +1,4 @@
import { autorun, observable } from "mobx";
import { action, autorun, observable } from "mobx";
import { ClampToEdgeWrapping, MathUtils, MirroredRepeatWrapping } from "three";
import { FixedNotZero } from "../../Common/Utils";
import { ObjectId } from "../../DatabaseServices/ObjectId";
@ -12,7 +12,10 @@ export class TextureStore
/**由于老板觉得这个不好算 已经改成贴图的尺寸了*/
repeatX = observable.box(1);
repeatY = observable.box(1);
moveX = observable.box(0);
moveY = observable.box(0);
@observable textureImg = "";
@observable imgUrl = "";
rotation = observable.box(0); //角度
textureId: ObjectId;
autorunFun: Function;
@ -28,6 +31,8 @@ export class TextureStore
this.warpS.get();
this.repeatX.get();
this.repeatY.get();
this.moveX.get();
this.moveY.get();
this.rotation.get();
this.UpdateEvent();
});
@ -50,13 +55,18 @@ export class TextureStore
this.wrapT.set(MirroredRepeatWrapping);
this.repeatX.set(1);
this.repeatY.set(1);
this.moveX.set(0);
this.moveY.set(0);
this.rotation.set(0);
this.textureImg = "";
this.imgUrl = "";
this.textureId = null;
}
}
private updateing = false;
@action
UpdateStore(textureId: ObjectId | TextureTableRecord)
{
if (!textureId) return;
@ -78,11 +88,14 @@ export class TextureStore
this.repeatX.set(1 / texture.repeatX);
this.repeatY.set(1 / texture.repeatY);
this.moveX.set(texture.moveX);
this.moveY.set(texture.moveY);
this.warpS.set(texture.WrapS);
this.wrapT.set(texture.WrapT);
let deg = MathUtils.radToDeg(texture.rotation);
this.rotation.set(parseFloat(FixedNotZero(deg, 1)));
this.textureImg = texture.imageUrl;
this.imgUrl = texture.imgUrl;
this.updateing = false;
}
@ -100,7 +113,10 @@ export class TextureStore
this.repeatX.set(1);
}
else
{
texture.repeatX = 1 / this.repeatX.get();
texture.moveX = this.moveX.get();
}
if (texture.WrapT === ClampToEdgeWrapping)
{
@ -108,7 +124,10 @@ export class TextureStore
this.repeatY.set(1);
}
else
{
texture.repeatY = 1 / this.repeatY.get();
texture.moveY = this.moveY.get();
}
}
}

Loading…
Cancel
Save