!260 服务端材质更新和应用

Merge pull request !260 from ZoeLeeFZ/optMatPanel2
pull/260/MERGE
ChenX 6 years ago
parent 6bb3f78d88
commit 585cccde37

@ -23,6 +23,7 @@ export const MaterialUrls = {
get: CURRENT_HOST + "/CAD-materialList",
detail: CURRENT_HOST + "/CAD-materialDetail",
delete: CURRENT_HOST + "/CAD-materialDelete",
update: CURRENT_HOST + "/CAD-materialUpdate",
}
export const ShopUrls = {

@ -1,6 +1,6 @@
export enum ZINDEX
{
Modal = "31",
Modal = "33", //自定义模态框需比蓝图的层级高
RightPanel = "29",
HighMasking = "30", //低于模态框一级的遮罩
Common = "20", //普通的

@ -50,56 +50,58 @@ const TextStyle: CSSProperties = {
width: "100%",
}
interface AssetPropsType
{
showObject: PhysicalMaterialRecord;
}
/**
*
*/
@observer
export class Asset extends React.Component<AssetPropsType, {}>{
export class Asset extends React.Component<{ material: PhysicalMaterialRecord }, {}>{
image: HTMLImageElement;
nameEl: HTMLDivElement;
removeCall: Function[] = [];
private update = async () =>
/**
*
*/
private UpdateRenderPreview = async () =>
{
let obj = this.props.showObject;
if (obj.map && obj.map.Object)
let material = this.props.material;
if (material.map && material.map.Object)
{
await (obj.map.Object as TextureTableRecord).Update();
await (material.map.Object as TextureTableRecord).Update();
}
MaterialRendererSingle.render(obj.Material, url => this.image.src = url);
this.nameEl.innerText = obj.Name;
MaterialRendererSingle.render(material.Material, (url: string) => this.image.src = url);
this.nameEl.innerText = material.Name;
}
private reset = () =>
private Destroy = () =>
{
this.removeCall.forEach(f => f());
this.removeCall.length = 0;
}
async componentDidMount()
{
await this.update();
this.removeCall.push(xaop.end(this.props.material, this.props.material.Update, this.UpdateRenderPreview));
//使用WblockClone时,材质先进来,Texture没进来,导致的显示错误.
setTimeout(() =>
{
this.props.material.Update();
}, 50);
}
componentWillUnmount()
{
this.reset();
this.Destroy();
}
handleDoubleClick = () =>
{
this.reset();
appUi.showMaterialEditor({ mat: this.props.showObject });
this.removeCall.push(xaop.end(this.props.showObject, this.props.showObject.Update, this.update));
this.UpdateRenderPreview();
appUi.showMaterialEditor({ material: this.props.material });
}
handleApply = () =>
{
//得到选择的实体.
let material = this.props.showObject as PhysicalMaterialRecord;
let material = this.props.material as PhysicalMaterialRecord;
for (let en of app.m_Editor.m_SelectCtrl.SelectSet.SelectEntityList)
{
en.Material = material.Id;
@ -108,7 +110,7 @@ export class Asset extends React.Component<AssetPropsType, {}>{
}
handleCollection = async () =>
{
let material = this.props.showObject as PhysicalMaterialRecord;
let material = this.props.material as PhysicalMaterialRecord;
let blob = await MaterialRendererSingle.getBlob(material.Material);
let file = new File([blob], "blob.png", { type: blob.type });
@ -146,7 +148,7 @@ export class Asset extends React.Component<AssetPropsType, {}>{
}
handleDelete = () =>
{
app.m_Database.MaterialTable.Remove(this.props.showObject as PhysicalMaterialRecord);
app.m_Database.MaterialTable.Remove(this.props.material as PhysicalMaterialRecord);
}
render()
{
@ -177,7 +179,7 @@ export class Asset extends React.Component<AssetPropsType, {}>{
<div
style={TextStyle}
ref={el => this.nameEl = el}
>{this.props.showObject.Name}</div>
>{this.props.material.Name}</div>
</div>
)
}

@ -6,11 +6,18 @@ import { ContentComponent } from './Content';
import { TopPanelStore } from '../../Store/TopPanelStore';
import { Card } from '@blueprintjs/core';
import { ZINDEX } from '../../../Common/ZIndex';
import { request } from '../../../Common/Request';
import { SignUrl } from '../../../Common/HostUrl';
@inject('store')
@observer
export class MainContent extends React.Component<{ store?: TopPanelStore }>
{
async componentWillMount()
{
//一开始就检查是否登陆
await request(SignUrl.heart);
}
render()
{
return (

@ -9,57 +9,55 @@ import { IObservableValue } from 'mobx';
import { IDirProps } from './SourceManage/CommonPanel';
import { MaterialRendererSingle } from '../Editor/Asset/MaterialRenderer';
import { request, RequestStatus } from '../../Common/Request';
import { MaterialOut, deflate } from './SourceManage/SerializeMaterial';
import { MaterialOut, deflate, MaterialIn, inflate } from './SourceManage/SerializeMaterial';
import { AppToaster } from './Toaster';
import { Database } from '../../DatabaseServices/Database';
import { ImgsUrl, MaterialUrls } from '../../Common/HostUrl';
export interface MaterialContainerProps
{
mat: PhysicalMaterialRecord;
material: PhysicalMaterialRecord;
onlineId?: string;
isNew?: boolean;
isOpen?: IObservableValue<boolean>;
currentDir?: IDirProps;
getData?: () => void;
}
export interface MaterialContainerState
{
isOpen: boolean;
}
export const MATCONTENTID = "matContent";
export class MaterialContainer extends React.Component<MaterialContainerProps, MaterialContainerState> {
med: MaterialEditorLayout;
/**
*
*/
export class MaterialContainer extends React.Component<MaterialContainerProps, { isOpen: boolean }> {
materialEditor: MaterialEditorLayout;
container: HTMLElement;
constructor(props)
{
super(props);
this.state = {
isOpen: true
}
};
}
handleOpen = () =>
{
if (this.props.isNew)
{
this.props.mat.Name = "新材质";
let db = new Database();
this.props.material.Name = db.MaterialTable.AllocateName();
db.MaterialTable.Add(this.props.material);
}
this.med = new MaterialEditorLayout(this.props.mat);
this.materialEditor = new MaterialEditorLayout(this.props.material);
}
handleClose = () =>
{
app.m_Editor.m_ModalManage.Clear();
if (this.props.isOpen)
this.props.isOpen.set(false);
if (this.props.onlineId)
{
//更新线上的值
console.log("update");
}
}
handleUploadMat = async () =>
private handleUploadLogo = async () =>
{
let material = this.props.mat;
let material = this.props.material;
let blob = await MaterialRendererSingle.getBlob(material.Material);
let file = new File([blob], "blob.png", { type: blob.type });
@ -75,11 +73,17 @@ export class MaterialContainer extends React.Component<MaterialContainerProps, M
{
logoPath = data.images.path;
}
return logoPath;
}
handleUploadMtl = async () =>
{
let material = this.props.material;
let logoPath = await this.handleUploadLogo();
let materialJson = MaterialOut(material);
data = await request(MaterialUrls.create, {
let data = await request(MaterialUrls.create, {
body: JSON.stringify({
dir_id: this.props.currentDir.id,
name: material.Name,
@ -95,6 +99,31 @@ export class MaterialContainer extends React.Component<MaterialContainerProps, M
}
this.handleClose();
}
handleUpdateMtl = async () =>
{
let material = this.props.material;
let logoPath = await this.handleUploadLogo();
let materialJson = MaterialOut(material);
let data = await request(MaterialUrls.update, {
body: JSON.stringify({
material_id: this.props.onlineId,
name: material.Name,
logo: logoPath,
file: deflate(materialJson),
zip_type: "gzip",
})
})
if (data.err_code === RequestStatus.Ok)
{
await this.props.getData();
AppToaster.show({
message: "更新材质成功",
timeout: 1000
});
}
this.handleClose();
}
componentDidMount()
{
this.handleOpen();
@ -106,7 +135,7 @@ export class MaterialContainer extends React.Component<MaterialContainerProps, M
if (e.keyCode === KeyBoard.Escape)
this.handleClose();
e.stopPropagation();
})
});
}
else
{
@ -115,12 +144,12 @@ export class MaterialContainer extends React.Component<MaterialContainerProps, M
if (e.keyCode === KeyBoard.Escape)
this.handleClose();
e.stopPropagation();
}))
}));
}
}
componentWillUnmount()
{
this.med.dispose();
this.materialEditor.dispose();
}
public render()
{
@ -168,7 +197,30 @@ export class MaterialContainer extends React.Component<MaterialContainerProps, M
<Button
className={Classes.INTENT_SUCCESS}
text="上传"
onClick={this.handleUploadMat} />
onClick={this.handleUploadMtl} />
<Button
className={Classes.INTENT_DANGER}
text="取消"
onClick={this.handleClose} />
</div>
</div>
}
{
this.props.onlineId &&
<div className={Classes.DIALOG_FOOTER} >
<div
className={Classes.DIALOG_FOOTER_ACTIONS}
style={{
justifyContent: "flex-end",
padding: "10px 0",
width: "100%",
}}
>
<Button
className={Classes.INTENT_SUCCESS}
text="更新"
onClick={this.handleUpdateMtl} />
<Button
className={Classes.INTENT_DANGER}

@ -32,20 +32,6 @@ export class MaterialExplorer extends React.Component<{ materialTable: MaterialT
super(p, s);
this.handleCreateMaterial();
}
renderMaterialList()
{
return [...this.props.materialTable.Materials.values()].map((o) =>
{
if (o instanceof PhysicalMaterialRecord)
return (
<Asset
key={o.Id.Index}
showObject={o}
/>
)
})
}
render()
{
return (
@ -63,7 +49,17 @@ export class MaterialExplorer extends React.Component<{ materialTable: MaterialT
});
}}
>
{this.renderMaterialList()}
{
[...this.props.materialTable.Materials.values()].map((material) =>
{
return (
<Asset
key={material.Id.Index}
material={material}
/>
)
})
}
</div>
)
}

@ -135,4 +135,5 @@
@import (less) "../../RightPanel/RightPanel.less";
@import (less) "./DoorModal.less";
@import (less) "./ReferenceCuttingModal.less";
@import (less) "../../../MaterialEditor//Material.less";

@ -2,20 +2,23 @@ import { Button, Card, Checkbox, Classes, Intent, Popover, Position } from '@blu
import { observer } from 'mobx-react';
import * as React from 'react';
import { request, RequestStatus } from '../../../Common/Request';
import { inflate } from './SerializeMaterial';
import { appUi } from '../../Layout/ApplicationLayout';
import { AppToaster } from '../Toaster';
import { inflate, MaterialIn, MaterialInAndAppendAppData } from './SerializeMaterial';
import { MaterialUrls, CURRENT_HOST } from '../../../Common/HostUrl';
export interface IImgListProps
{
deleteFun?: (mat) => void;
deleteFun?: (mtl) => void;
dataList?: any[];
select?: (e, data) => void;
getData?: () => void;
}
@observer
export class MaterialList extends React.Component<IImgListProps, {}> {
private disposeEvent: Function;
handleDbClick = async (mat: { material_id: string }) =>
private readyMtl;
private handleGetMtlJson = async (mat: { material_id: string }): Promise<string | undefined> =>
{
let material_id = mat.material_id;
let data = await request(MaterialUrls.detail, {
@ -27,21 +30,48 @@ export class MaterialList extends React.Component<IImgListProps, {}> {
if (data.err_code === RequestStatus.Ok)
{
let file = data.materials.file;
let json = inflate(file);
// let mat = MaterialIn(JSON.parse(json));
// let testMat = new PhysicalMaterialRecord();
// testMat.Name = app.m_Database.MaterialTable.AllocateName();
// // app.m_Database.MaterialTable.Add(testMat);
// appUi.showMaterialEditor({ mat: mat, onlineId: data.materials.material_id });
return inflate(file);
}
}
private handleDbClick = async (data: { material_id: string }) =>
{
let json = await this.handleGetMtlJson(data);
if (json)
{
let mtl = MaterialIn(JSON.parse(json));
appUi.showMaterialEditor({ material: mtl, onlineId: data.material_id, getData: this.props.getData });
}
// this.disposeEvent = end(app.m_Editor.m_ModalManage, app.m_Editor.m_ModalManage.Clear, () =>
// {
// console.log("close");
// this.disposeEvent();
// });
}
private handleDragStart = (mtl: any) =>
{
this.readyMtl = mtl;
}
private handleApply = async (e: DragEvent) =>
{
if ((e.target as HTMLElement).classList.contains(Classes.OVERLAY_BACKDROP))
{
e.dataTransfer.dropEffect = 'link';
let json = await this.handleGetMtlJson(this.readyMtl);
if (json)
{
MaterialInAndAppendAppData(JSON.parse(json));
this.readyMtl = null;
AppToaster.show({
message: "材质已应用到图纸",
timeout: 1000
});
}
}
}
componentDidMount()
{
document.addEventListener("drop", this.handleApply);
}
componentWillUnmount()
{
document.removeEventListener("drop", this.handleApply);
}
public render()
{
return (
@ -49,19 +79,20 @@ export class MaterialList extends React.Component<IImgListProps, {}> {
className="mat-list"
>
{
this.props.dataList.map(mat =>
this.props.dataList.map(mtl =>
{
return (
<li
key={mat.image_id}
key={mtl.image_id}
onDragStart={() => this.handleDragStart(mtl)}
>
<div
className="look-mat editor-lint"
onDoubleClick={() => this.handleDbClick(mat)}
onDoubleClick={() => this.handleDbClick(mtl)}
>
<img src={`${CURRENT_HOST}/${mat.logo}`} />
<img src={`${CURRENT_HOST}/${mtl.logo}`} />
</div>
<p title={mat.name}>{mat.name}</p>
<p title={mtl.name}>{mtl.name}</p>
<Popover
position={Position.RIGHT}
content={
@ -74,7 +105,7 @@ export class MaterialList extends React.Component<IImgListProps, {}> {
<Button
className={Classes.POPOVER_DISMISS}
intent={Intent.PRIMARY}
onClick={() => this.props.deleteFun(mat)}
onClick={() => this.props.deleteFun(mtl)}
text="确定" />
</div>
</Card>
@ -85,12 +116,12 @@ export class MaterialList extends React.Component<IImgListProps, {}> {
/>}
/>
<Checkbox
className={mat.isChecked && "selected"}
className={mtl.isChecked && "selected"}
inline={true}
checked={mat.isChecked}
checked={mtl.isChecked}
onChange={e =>
{
this.props.select(e, mat);
this.props.select(e, mtl);
}}
/>
</li>

@ -33,7 +33,7 @@ export class MaterialPanel extends React.Component<IMaterialProps, IMaterialStat
handleAddMaterial = () =>
{
let material = new PhysicalMaterialRecord();
appUi.showMaterialEditor({ mat: material, isNew: true });
appUi.showMaterialEditor({ material: material, isNew: true });
}
renderNav()
{
@ -79,7 +79,7 @@ export class MaterialPanel extends React.Component<IMaterialProps, IMaterialStat
{
this.startCreateMat.get() &&
<MaterialContainer
mat={new PhysicalMaterialRecord()}
material={new PhysicalMaterialRecord()}
isNew={true}
isOpen={this.startCreateMat}
/>

@ -20,9 +20,15 @@ export function MaterialIn(fileData: Object[])
{
let f = new CADFiler(fileData);
let db = new Database().FileRead(f);
return db.MaterialTable.Symbols.entries().next().value[1]
}
/**反序列化材质并加入图纸*/
export function MaterialInAndAppendAppData(fileData: Object[])
{
let mtl = MaterialIn(fileData) as PhysicalMaterialRecord;
app.m_Database.WblockCloneObejcts(
[db.MaterialTable.Symbols.entries().next().value[1]],
[mtl],
app.m_Database.MaterialTable,
new Map(),
DuplicateRecordCloning.Rename
@ -48,10 +54,11 @@ export function inflate(data: string)
function stringToUint8Array(str: string)
{
let encodeStr = encodeURI(str);
let arr = [];
for (let i = 0, j = str.length; i < j; ++i)
for (let i = 0; i < encodeStr.length; i++)
{
arr.push(str.charCodeAt(i));
arr.push(encodeStr.charCodeAt(i));
}
return new Uint8Array(arr);
@ -65,5 +72,5 @@ function Uint8ArrayToString(fileData: number[])
dataString += String.fromCharCode(fileData[i]);
}
return dataString;
return decodeURI(dataString);
}

@ -55,6 +55,7 @@ export default class SoucePanel extends React.Component<{ store?: TopPanelStore
}
return (
<Dialog
enforceFocus={false}
icon="inbox"
isOpen={this.props.store.m_FileManageOpen}
onClose={() => this.props.store.m_FileManageOpen = false}

@ -107,20 +107,21 @@
}
}
.mat-list:first-child{
li{
cursor: pointer;
}
.look-mat{
height: 100%;
user-select:none;
}
.look-mat:hover::before{
content: "双击编辑材质";
content: "双击编辑材质 拖至绘图区应用";
position: absolute;
left: 0;
right: 0;
bottom: 0;
top: 0;
left:20%;
top: 10px;
margin: auto;
width: max-content;
height: 20px;
width: 55%;
height: 30px;
opacity: 0.5;
}
&>li{

@ -107,7 +107,7 @@ export class MaterialEditorLayout
componentName: ContainerComponenName,
componentState: { id: "SceneConfig" },
width: attPercent,
height: 8
height: 6
}
]
}

@ -1,99 +1,4 @@
//材质球编辑器 样式文件.
.flex {
display: flex;
}
.flexCol {
flex-direction: column;
}
.flexRow {
flex-direction: row;
}
.center {
align-self: center;
}
.matinput {
display: flex;
width: 200px;
margin: 3px;
color: white;
.prompt {
width: 65px;
overflow: hidden;
align-self: center;
box-sizing: border-box;
}
.input1 {
box-sizing: border-box;
align-self: center;
flex-basis: 0%;
flex-grow: 1;
overflow: hidden;
display: flex;
flex-direction: row;
width: 100%;
}
.input2 {
box-sizing: border-box; // border: 0;
width: 40px;
align-self: center;
overflow: hidden;
margin: auto;
}
.input2[type="checkbox"] {
width: 15px;
margin: 0;
}
}
.matinput_check {
.matinput();
.input2 {
width: 45px
}
}
.TextureInput {
width: 250px;
height: 100px;
display: flex;
margin: 3px;
flex-direction: row;
.img {
width: 100px;
background: #ea971a;
overflow: hidden;
box-sizing: border-box;
}
.att {
display: flex;
flex-grow: 1; // background: blanchedalmond;
flex-direction: column;
overflow: hidden;
flex-basis: 0%;
.repinput {
flex-grow: 1;
display: flex;
flex-direction: column;
overflow: hidden;
flex-basis: 0%;
}
.matinput {
.matinput;
width: auto;
.prompt {
width: 45px
}
}
}
}
.repeat {
display: flex;
}
#modal{
.texture-list{
height: calc(~"100% - 50px");
@ -166,6 +71,11 @@
.img-select-hint{
position: relative;
user-select: none;
overflow: hidden;
}
.img-select-hint:hover{
cursor: pointer;
}
.img-select-hint:hover:before{
content: "双击选择图片";
@ -176,8 +86,9 @@
top: 0;
margin: auto;
width: max-content;
height: 20px;
height: 33px;
opacity: 0.5;
z-index: 33;
}
.select-shop{
@ -189,8 +100,9 @@
}
&>div{
button{
height: 25px;
min-height: 25px;
height: 22px;
min-height: 22px;
padding: 0 10px;
}
input{
vertical-align: middle;

@ -48,21 +48,31 @@ export class Input extends React.Component<
onChange={e =>
{
if (typeof value.get() === "string")
{
value.set(e.target.value);
}
else
{
let val = parseFloat(e.target.value);
if (!isNaN(val))
value.set(val)
value.set(val);
}
}}
onBlur={e =>
{
if (typeof value.get() === "string" && e.target.value === "")
if (typeof value.get() === "number")
{
if (isNaN(parseFloat(e.target.value)))
{
this.inputEl.value = e.target.defaultValue;
value.set(parseFloat(e.target.defaultValue));
}
}
else
{
value.set(e.target.defaultValue);
if (e.target.value === "")
{
value.set(e.target.defaultValue);
this.inputEl.value = e.target.defaultValue;
}
}
}}
/>

@ -39,9 +39,11 @@ export class Texture extends React.Component<ITextureProps, {}>{
store.currentEditorStore = textureStore;
if (!textureStore.textureId)
{
let db = store.Material.Db;
let newTexture = new TextureTableRecord();
newTexture.Name = app.m_Database.TextureTable.AllocateName();
app.m_Database.TextureTable.Add(newTexture);
newTexture.Name = db.TextureTable.AllocateName();
db.TextureTable.Add(newTexture);
textureStore.textureId = newTexture.Id;
store.BindTextureId(newTexture.Id);
}
@ -100,12 +102,12 @@ export class Texture extends React.Component<ITextureProps, {}>{
</Label>
<div style={{ margin: 3, display: "flex" }}>
<span style={{ ...PromptStyle, width: 50 }}></span>
<span style={SpanStyle}>X:</span>
<span style={SpanStyle}>X</span>
<Input
value={textureStore.repeatX}
disabled={textureStore.warpS.get() === ClampToEdgeWrapping}
/>
<span style={SpanStyle}>Y:</span>
<span style={SpanStyle}>Y</span>
<Input
value={textureStore.repeatY}
disabled={textureStore.wrapT.get() === ClampToEdgeWrapping}
@ -113,9 +115,9 @@ export class Texture extends React.Component<ITextureProps, {}>{
</div>
<div style={{ margin: 3, display: "flex" }}>
<span style={{ ...PromptStyle, width: 50 }}></span>
<span style={SpanStyle}>:</span>
<span style={SpanStyle}></span>
<input style={InputStyle} />
<span style={SpanStyle}>:</span>
<span style={SpanStyle}></span>
<input style={InputStyle} />
</div>
</div>

@ -89,7 +89,7 @@ export class MaterialStore extends Singleton
this.bumpMaping.dispose();
this.roughnessMaping.dispose();
}
protected Material: PhysicalMaterialRecord;
Material: PhysicalMaterialRecord;
BindMaterial(material: PhysicalMaterialRecord)
{
this.Material = material;
@ -129,10 +129,6 @@ export class MaterialStore extends Singleton
this.bumpMaping.UpdateStore(this.bumpMaping.textureId);
this.roughnessMaping.UpdateStore(this.roughnessMaping.textureId);
this.textureMaping.UpdateStore(this.textureMaping.textureId);
this.bumpMaping.UpdateStore(this.bumpMaping.textureId);
this.roughnessMaping.UpdateStore(this.roughnessMaping.textureId);
this.Material.Update();
await (MaterialEditor.GetInstance() as MaterialEditor).Update();
}
@ -191,8 +187,15 @@ export class MaterialStore extends Singleton
return;
}
this.isOpenTexture = false;
this.currentEditorStore.InitStore(texture);
this.BindTextureId(texture.Id);
let db = this.Material.Db;
let textureClone = texture;
if (db !== app.m_Database)
{
textureClone = texture.Clone();
db.TextureTable.Add(textureClone);
}
this.currentEditorStore.InitStore(textureClone);
this.BindTextureId(textureClone.Id);
this.UpdateMaterial();
}
ChangeTextureImg(pic)

@ -2,6 +2,7 @@ import { autorun, observable } from "mobx";
import { MirroredRepeatWrapping, ClampToEdgeWrapping, Math } from "three";
import { ObjectId } from "../../DatabaseServices/ObjectId";
import { TextureTableRecord } from "../../DatabaseServices/Texture";
import { FixedNotZero } from "../../Common/Utils";
export class TextureStore
@ -74,7 +75,8 @@ export class TextureStore
this.repeatY.set(texture.repeatY);
this.warpS.set(texture.wrapS);
this.wrapT.set(texture.wrapT);
this.rotation.set(Math.radToDeg(texture.rotation));
let deg = Math.radToDeg(texture.rotation);
this.rotation.set(parseFloat(FixedNotZero(deg, 1)));
this.textureImg = texture.imageUrl;
}
UpdateEvent()

Loading…
Cancel
Save