texturePropertyTabComponent.tsx 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. import * as React from "react";
  2. import { BaseTexture } from 'babylonjs/Materials/Textures/baseTexture';
  3. import { FileButtonLineComponent } from '../../sharedComponents/fileButtonLineComponent';
  4. import { Tools } from 'babylonjs/Misc/tools';
  5. import { LineContainerComponent } from '../../sharedComponents/lineContainerComponent';
  6. import { TextInputLineComponent } from '../../sharedComponents/textInputLineComponent';
  7. import { CheckBoxLineComponent } from '../../sharedComponents/checkBoxLineComponent';
  8. import { Texture } from 'babylonjs/Materials/Textures/texture';
  9. import { SliderLineComponent } from '../../sharedComponents/sliderLineComponent';
  10. import { FloatLineComponent } from '../../sharedComponents/floatLineComponent';
  11. import { ButtonLineComponent } from '../../sharedComponents/buttonLineComponent';
  12. import { CubeTexture } from 'babylonjs/Materials/Textures/cubeTexture';
  13. import { OptionsLineComponent } from '../../sharedComponents/optionsLineComponent';
  14. import { IPropertyComponentProps } from './propertyComponentProps';
  15. import { ReflectionTextureBlock } from 'babylonjs/Materials/Node/Blocks/Dual/reflectionTextureBlock';
  16. import { ReflectionBlock } from 'babylonjs/Materials/Node/Blocks/PBR/reflectionBlock';
  17. import { RefractionBlock } from 'babylonjs/Materials/Node/Blocks/PBR/refractionBlock';
  18. import { TextureBlock } from 'babylonjs/Materials/Node/Blocks/Dual/textureBlock';
  19. import { CurrentScreenBlock } from 'babylonjs/Materials/Node/Blocks/Dual/currentScreenBlock';
  20. import { ParticleTextureBlock } from 'babylonjs/Materials/Node/Blocks/Particle/particleTextureBlock';
  21. import { GeneralPropertyTabComponent, GenericPropertyTabComponent } from './genericNodePropertyComponent';
  22. import { NodeMaterialModes } from 'babylonjs/Materials/Node/Enums/nodeMaterialModes';
  23. type ReflectionTexture = ReflectionTextureBlock | ReflectionBlock | RefractionBlock;
  24. type AnyTexture = TextureBlock | ReflectionTexture | CurrentScreenBlock | ParticleTextureBlock;
  25. export class TexturePropertyTabComponent extends React.Component<IPropertyComponentProps, {isEmbedded: boolean, loadAsCubeTexture: boolean, textureIsPrefiltered: boolean}> {
  26. get textureBlock(): AnyTexture {
  27. return this.props.block as AnyTexture;
  28. }
  29. constructor(props: IPropertyComponentProps) {
  30. super(props);
  31. let texture = this.textureBlock.texture as BaseTexture;
  32. this.state = {isEmbedded: !texture || texture.name.substring(0, 4) === "data", loadAsCubeTexture: texture && texture.isCube, textureIsPrefiltered: true};
  33. }
  34. UNSAFE_componentWillUpdate(nextProps: IPropertyComponentProps, nextState: {isEmbedded: boolean, loadAsCubeTexture: boolean}) {
  35. if (nextProps.block !== this.props.block) {
  36. let texture = (nextProps.block as AnyTexture).texture as BaseTexture;
  37. nextState.isEmbedded = !texture || texture.name.substring(0, 4) === "data";
  38. nextState.loadAsCubeTexture = texture && texture.isCube;
  39. }
  40. }
  41. private _generateRandomForCache() {
  42. return 'xxxxxxxxxxxxxxxxxxxx'.replace(/[x]/g, (c) => {
  43. var r = Math.random() * 10 | 0;
  44. return r.toString();
  45. });
  46. }
  47. updateAfterTextureLoad() {
  48. this.props.globalState.onUpdateRequiredObservable.notifyObservers();
  49. this.props.globalState.onRebuildRequiredObservable.notifyObservers();
  50. this.forceUpdate();
  51. }
  52. removeTexture() {
  53. let texture = this.textureBlock.texture as BaseTexture;
  54. if (texture) {
  55. texture.dispose();
  56. (texture as any) = null;
  57. this.textureBlock.texture = null;
  58. }
  59. this.updateAfterTextureLoad();
  60. }
  61. _prepareTexture() {
  62. let texture = this.textureBlock.texture as BaseTexture;
  63. if (texture && texture.isCube !== this.state.loadAsCubeTexture) {
  64. texture.dispose();
  65. (texture as any) = null;
  66. }
  67. if (!texture) {
  68. if (!this.state.loadAsCubeTexture) {
  69. this.textureBlock.texture = new Texture(null, this.props.globalState.nodeMaterial.getScene(), false,
  70. this.textureBlock instanceof ReflectionTextureBlock || this.textureBlock instanceof ReflectionBlock || this.textureBlock instanceof RefractionBlock || this.props.globalState.mode === NodeMaterialModes.PostProcess);
  71. texture = this.textureBlock.texture;
  72. texture.coordinatesMode = Texture.EQUIRECTANGULAR_MODE;
  73. } else {
  74. this.textureBlock.texture = new CubeTexture("", this.props.globalState.nodeMaterial.getScene());
  75. texture = this.textureBlock.texture;
  76. texture.coordinatesMode = Texture.CUBIC_MODE;
  77. }
  78. }
  79. }
  80. /**
  81. * Replaces the texture of the node
  82. * @param file the file of the texture to use
  83. */
  84. replaceTexture(file: File) {
  85. this._prepareTexture();
  86. let texture = this.textureBlock.texture as BaseTexture;
  87. Tools.ReadFile(file, (data) => {
  88. var blob = new Blob([data], { type: "octet/stream" });
  89. var reader = new FileReader();
  90. reader.readAsDataURL(blob);
  91. reader.onloadend = () => {
  92. let base64data = reader.result as string;
  93. let extension: string | undefined = undefined;
  94. if (file.name.toLowerCase().indexOf(".dds") > 0) {
  95. extension = ".dds";
  96. } else if (file.name.toLowerCase().indexOf(".env") > 0) {
  97. extension = ".env";
  98. }
  99. if (texture.isCube) {
  100. (texture as CubeTexture).updateURL(base64data, extension, () => this.updateAfterTextureLoad(), this.state.textureIsPrefiltered);
  101. } else {
  102. (texture as Texture).updateURL(base64data, extension, () => this.updateAfterTextureLoad());
  103. }
  104. }
  105. }, undefined, true);
  106. }
  107. replaceTextureWithUrl(url: string) {
  108. this._prepareTexture();
  109. let texture = this.textureBlock.texture as BaseTexture;
  110. if (texture.isCube || this.textureBlock instanceof ReflectionTextureBlock || this.textureBlock instanceof ReflectionBlock || this.textureBlock instanceof RefractionBlock) {
  111. let extension: string | undefined = undefined;
  112. if (url.toLowerCase().indexOf(".dds") > 0) {
  113. extension = ".dds";
  114. } else if (url.toLowerCase().indexOf(".env") > 0) {
  115. extension = ".env";
  116. }
  117. (texture as CubeTexture).updateURL(url, extension, () => this.updateAfterTextureLoad(), this.state.textureIsPrefiltered);
  118. } else {
  119. (texture as Texture).updateURL(url, null, () => this.updateAfterTextureLoad());
  120. }
  121. }
  122. render() {
  123. let url = "";
  124. let texture = this.textureBlock.texture as BaseTexture;
  125. if (texture && texture.name && texture.name.substring(0, 4) !== "data") {
  126. url = texture.name;
  127. }
  128. url = url.replace(/\?nocache=\d+/, "");
  129. let isInReflectionMode = this.textureBlock instanceof ReflectionTextureBlock || this.textureBlock instanceof ReflectionBlock || this.textureBlock instanceof RefractionBlock;
  130. let isFrozenTexture = this.textureBlock instanceof CurrentScreenBlock || this.textureBlock instanceof ParticleTextureBlock;
  131. let showIsInGammaSpace = this.textureBlock instanceof ReflectionBlock;
  132. var reflectionModeOptions: {label: string, value: number}[] = [
  133. {
  134. label: "Cubic", value: Texture.CUBIC_MODE
  135. },
  136. {
  137. label: "Equirectangular", value: Texture.EQUIRECTANGULAR_MODE
  138. },
  139. {
  140. label: "Explicit", value: Texture.EXPLICIT_MODE
  141. },
  142. {
  143. label: "Fixed equirectangular", value: Texture.FIXED_EQUIRECTANGULAR_MODE
  144. },
  145. {
  146. label: "Fixed mirrored equirectangular", value: Texture.FIXED_EQUIRECTANGULAR_MIRRORED_MODE
  147. },
  148. {
  149. label: "Planar", value: Texture.PLANAR_MODE
  150. },
  151. {
  152. label: "Projection", value: Texture.PROJECTION_MODE
  153. },
  154. {
  155. label: "Skybox", value: Texture.SKYBOX_MODE
  156. },
  157. {
  158. label: "Spherical", value: Texture.SPHERICAL_MODE
  159. },
  160. ];
  161. return (
  162. <div>
  163. <GeneralPropertyTabComponent globalState={this.props.globalState} block={this.props.block}/>
  164. <LineContainerComponent title="PROPERTIES">
  165. <CheckBoxLineComponent label="Auto select UV" propertyName="autoSelectUV" target={this.props.block} onValueChanged={() => {
  166. this.props.globalState.onUpdateRequiredObservable.notifyObservers();
  167. }}/>
  168. {
  169. texture && !isInReflectionMode &&
  170. <CheckBoxLineComponent label="Convert to gamma space" propertyName="convertToGammaSpace" target={this.props.block} onValueChanged={() => {
  171. this.props.globalState.onUpdateRequiredObservable.notifyObservers();
  172. }}/>
  173. }
  174. {
  175. texture && !isInReflectionMode &&
  176. <CheckBoxLineComponent label="Convert to linear space" propertyName="convertToLinearSpace" target={this.props.block} onValueChanged={() => {
  177. this.props.globalState.onUpdateRequiredObservable.notifyObservers();
  178. }}/>
  179. }
  180. {
  181. texture && showIsInGammaSpace &&
  182. <CheckBoxLineComponent label="Is in gamma space" propertyName="gammaSpace" target={texture} onValueChanged={() => {
  183. this.props.globalState.onUpdateRequiredObservable.notifyObservers();
  184. }}/>
  185. }
  186. {
  187. texture && isInReflectionMode &&
  188. <OptionsLineComponent label="Reflection mode" options={reflectionModeOptions} target={texture} propertyName="coordinatesMode" onSelect={(value: any) => {
  189. texture.coordinatesMode = value;
  190. this.forceUpdate();
  191. this.props.globalState.onUpdateRequiredObservable.notifyObservers();
  192. }} />
  193. }
  194. {
  195. texture && !isInReflectionMode && !isFrozenTexture &&
  196. <CheckBoxLineComponent label="Clamp U" isSelected={() => texture.wrapU === Texture.CLAMP_ADDRESSMODE} onSelect={(value) => {
  197. texture.wrapU = value ? Texture.CLAMP_ADDRESSMODE : Texture.WRAP_ADDRESSMODE;
  198. this.props.globalState.onUpdateRequiredObservable.notifyObservers();
  199. }} />
  200. }
  201. {
  202. texture && !isInReflectionMode && !isFrozenTexture &&
  203. <CheckBoxLineComponent label="Clamp V" isSelected={() => texture.wrapV === Texture.CLAMP_ADDRESSMODE} onSelect={(value) => {
  204. texture.wrapV = value ? Texture.CLAMP_ADDRESSMODE : Texture.WRAP_ADDRESSMODE;
  205. this.props.globalState.onUpdateRequiredObservable.notifyObservers();
  206. }} />
  207. }
  208. {
  209. texture && !isInReflectionMode && !isFrozenTexture &&
  210. <FloatLineComponent globalState={this.props.globalState} label="Offset U" target={texture} propertyName="uOffset"
  211. onChange={() => {
  212. this.props.globalState.onUpdateRequiredObservable.notifyObservers();
  213. }}
  214. />
  215. }
  216. {
  217. texture && !isInReflectionMode && !isFrozenTexture &&
  218. <FloatLineComponent globalState={this.props.globalState} label="Offset V" target={texture} propertyName="vOffset"
  219. onChange={() => {
  220. this.props.globalState.onUpdateRequiredObservable.notifyObservers();
  221. }}
  222. />
  223. }
  224. {
  225. texture && !isInReflectionMode && !isFrozenTexture &&
  226. <FloatLineComponent globalState={this.props.globalState} label="Scale U" target={texture} propertyName="uScale"
  227. onChange={() => {
  228. this.props.globalState.onUpdateRequiredObservable.notifyObservers();
  229. }} />
  230. }
  231. {
  232. texture && !isInReflectionMode && !isFrozenTexture &&
  233. <FloatLineComponent globalState={this.props.globalState} label="Scale V" target={texture} propertyName="vScale"
  234. onChange={() => {
  235. this.props.globalState.onUpdateRequiredObservable.notifyObservers();
  236. }} />
  237. }
  238. {
  239. texture && !isInReflectionMode && !isFrozenTexture &&
  240. <SliderLineComponent label="Rotation U" target={texture} globalState={this.props.globalState} propertyName="uAng" minimum={0} maximum={Math.PI * 2} useEuler={true} step={0.1}
  241. onChange={() => {
  242. this.props.globalState.onUpdateRequiredObservable.notifyObservers();
  243. }}
  244. />
  245. }
  246. {
  247. texture && !isInReflectionMode && !isFrozenTexture &&
  248. <SliderLineComponent label="Rotation V" target={texture} globalState={this.props.globalState} propertyName="vAng" minimum={0} maximum={Math.PI * 2} useEuler={true} step={0.1}
  249. onChange={() => {
  250. this.props.globalState.onUpdateRequiredObservable.notifyObservers();
  251. }}
  252. />
  253. }
  254. {
  255. texture && !isInReflectionMode && !isFrozenTexture &&
  256. <SliderLineComponent label="Rotation W" target={texture} globalState={this.props.globalState} propertyName="wAng" minimum={0} maximum={Math.PI * 2} useEuler={true} step={0.1}
  257. onChange={() => {
  258. this.props.globalState.onUpdateRequiredObservable.notifyObservers();
  259. }}
  260. />
  261. }
  262. </LineContainerComponent>
  263. <LineContainerComponent title="SOURCE">
  264. <CheckBoxLineComponent label="Embed static texture" isSelected={() => this.state.isEmbedded} onSelect={value => {
  265. this.setState({isEmbedded: value});
  266. this.textureBlock.texture = null;
  267. this.updateAfterTextureLoad();
  268. }}/>
  269. {
  270. isInReflectionMode &&
  271. <CheckBoxLineComponent label="Load as cube texture" isSelected={() => this.state.loadAsCubeTexture}
  272. onSelect={value => this.setState({loadAsCubeTexture: value})}/>
  273. }
  274. {
  275. isInReflectionMode && this.state.loadAsCubeTexture &&
  276. <CheckBoxLineComponent label="&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Texture is prefiltered" isSelected={() => this.state.textureIsPrefiltered}
  277. onSelect={value => this.setState({textureIsPrefiltered: value})}/>
  278. }
  279. {
  280. this.state.isEmbedded &&
  281. <FileButtonLineComponent label="Upload" onClick={(file) => this.replaceTexture(file)} accept=".jpg, .png, .tga, .dds, .env" />
  282. }
  283. {
  284. !this.state.isEmbedded &&
  285. <TextInputLineComponent label="Link" globalState={this.props.globalState} value={url} onChange={newUrl => this.replaceTextureWithUrl(newUrl)}/>
  286. }
  287. {
  288. !this.state.isEmbedded && url &&
  289. <ButtonLineComponent label="Refresh" onClick={() => this.replaceTextureWithUrl(url + "?nocache=" + this._generateRandomForCache())}/>
  290. }
  291. {
  292. texture &&
  293. <ButtonLineComponent label="Remove" onClick={() => this.removeTexture()}/>
  294. }
  295. </LineContainerComponent>
  296. <GenericPropertyTabComponent globalState={this.props.globalState} block={this.props.block}/>
  297. </div>
  298. );
  299. }
  300. }