texturePropertyTabComponent.tsx 15 KB

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