toolsTabComponent.tsx 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. import * as React from "react";
  2. import { PaneComponent, IPaneComponentProps } from "../paneComponent";
  3. import { LineContainerComponent } from "../lineContainerComponent";
  4. import { ButtonLineComponent } from "../lines/buttonLineComponent";
  5. import { Node } from "babylonjs/node";
  6. import { Nullable } from "babylonjs/types";
  7. import { VideoRecorder } from "babylonjs/Misc/videoRecorder";
  8. import { Tools } from "babylonjs/Misc/tools";
  9. import { EnvironmentTextureTools } from "babylonjs/Misc/environmentTextureTools";
  10. import { BackgroundMaterial } from "babylonjs/Materials/Background/backgroundMaterial";
  11. import { StandardMaterial } from "babylonjs/Materials/standardMaterial";
  12. import { PBRMaterial } from "babylonjs/Materials/PBR/pbrMaterial";
  13. import { CubeTexture } from "babylonjs/Materials/Textures/cubeTexture";
  14. import { Texture } from "babylonjs/Materials/Textures/texture";
  15. import { SceneSerializer } from "babylonjs/Misc/sceneSerializer";
  16. import { Mesh } from "babylonjs/Meshes/mesh";
  17. import { GLTFComponent } from "./tools/gltfComponent";
  18. import { GLTFData, GLTF2Export } from "babylonjs-serializers/glTF/2.0/index";
  19. import { FloatLineComponent } from '../lines/floatLineComponent';
  20. import { IScreenshotSize } from 'babylonjs/Misc/interfaces/screenshotSize';
  21. import { NumericInputComponent } from '../lines/numericInputComponent';
  22. import { CheckBoxLineComponent } from '../lines/checkBoxLineComponent';
  23. import { TextLineComponent } from '../lines/textLineComponent';
  24. export class ToolsTabComponent extends PaneComponent {
  25. private _videoRecorder: Nullable<VideoRecorder>;
  26. private _screenShotSize: IScreenshotSize = { precision: 1 };
  27. private _useWidthHeight = false;
  28. private _isExporting = false;
  29. constructor(props: IPaneComponentProps) {
  30. super(props);
  31. this.state = { tag: "Record video" };
  32. }
  33. componentDidMount() {
  34. if (!(BABYLON as any).GLTF2Export) {
  35. Tools.LoadScript("https://preview.babylonjs.com/serializers/babylonjs.serializers.min.js", () => {
  36. });
  37. return;
  38. }
  39. }
  40. componentWillUnmount() {
  41. if (this._videoRecorder) {
  42. this._videoRecorder.stopRecording();
  43. this._videoRecorder.dispose();
  44. this._videoRecorder = null;
  45. }
  46. }
  47. captureScreenshot() {
  48. const scene = this.props.scene;
  49. if (scene.activeCamera) {
  50. Tools.CreateScreenshot(scene.getEngine(), scene.activeCamera, this._screenShotSize);
  51. }
  52. }
  53. captureRender() {
  54. const scene = this.props.scene;
  55. const oldScreenshotSize: IScreenshotSize = {
  56. height: this._screenShotSize.height,
  57. width: this._screenShotSize.width,
  58. precision: this._screenShotSize.precision
  59. };
  60. if (!this._useWidthHeight) {
  61. this._screenShotSize.width = undefined;
  62. this._screenShotSize.height = undefined;
  63. }
  64. if (scene.activeCamera) {
  65. Tools.CreateScreenshotUsingRenderTarget(scene.getEngine(), scene.activeCamera, this._screenShotSize);
  66. }
  67. this._screenShotSize = oldScreenshotSize;
  68. }
  69. recordVideo() {
  70. if (this._videoRecorder && this._videoRecorder.isRecording) {
  71. this._videoRecorder.stopRecording();
  72. return;
  73. }
  74. const scene = this.props.scene;
  75. if (!this._videoRecorder) {
  76. this._videoRecorder = new VideoRecorder(scene.getEngine());
  77. }
  78. this._videoRecorder.startRecording().then(() => {
  79. this.setState({ tag: "Record video" });
  80. });
  81. this.setState({ tag: "Stop recording" });
  82. }
  83. shouldExport(node: Node): boolean {
  84. // No skybox
  85. if (node instanceof Mesh) {
  86. if (node.material) {
  87. const material = node.material as PBRMaterial | StandardMaterial | BackgroundMaterial;
  88. const reflectionTexture = material.reflectionTexture;
  89. if (reflectionTexture && reflectionTexture.coordinatesMode === Texture.SKYBOX_MODE) {
  90. return false;
  91. }
  92. }
  93. }
  94. return true;
  95. }
  96. exportGLTF() {
  97. const scene = this.props.scene;
  98. this._isExporting = true;
  99. this.forceUpdate();
  100. GLTF2Export.GLBAsync(scene, "scene", {
  101. shouldExportNode: (node) => this.shouldExport(node)
  102. }).then((glb: GLTFData) => {
  103. glb.downloadFiles();
  104. this._isExporting = false;
  105. this.forceUpdate();
  106. }).catch(reason => {
  107. this._isExporting = false;
  108. this.forceUpdate();
  109. });
  110. }
  111. exportBabylon() {
  112. const scene = this.props.scene;
  113. var strScene = JSON.stringify(SceneSerializer.Serialize(scene));
  114. var blob = new Blob([strScene], { type: "octet/stream" });
  115. Tools.Download(blob, "scene.babylon");
  116. }
  117. createEnvTexture() {
  118. const scene = this.props.scene;
  119. EnvironmentTextureTools.CreateEnvTextureAsync(scene.environmentTexture as CubeTexture)
  120. .then((buffer: ArrayBuffer) => {
  121. var blob = new Blob([buffer], { type: "octet/stream" });
  122. Tools.Download(blob, "environment.env");
  123. })
  124. .catch((error: any) => {
  125. console.error(error);
  126. alert(error);
  127. });
  128. }
  129. resetReplay() {
  130. this.props.globalState.recorder.reset();
  131. }
  132. exportReplay() {
  133. this.props.globalState.recorder.export();
  134. }
  135. render() {
  136. const scene = this.props.scene;
  137. if (!scene) {
  138. return null;
  139. }
  140. return (
  141. <div className="pane">
  142. <LineContainerComponent globalState={this.props.globalState} title="CAPTURE">
  143. <ButtonLineComponent label="Screenshot" onClick={() => this.captureScreenshot()} />
  144. <ButtonLineComponent label={this.state.tag} onClick={() => this.recordVideo()} />
  145. </LineContainerComponent>
  146. <LineContainerComponent globalState={this.props.globalState} title="CAPTURE WITH RTT">
  147. <ButtonLineComponent label="Capture" onClick={() => this.captureRender()} />
  148. <div className="vector3Line">
  149. <FloatLineComponent label="Precision" target={this._screenShotSize} propertyName='precision' onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  150. <CheckBoxLineComponent label="Use Width/Height" onSelect={ value => {
  151. this._useWidthHeight = value;
  152. this.forceUpdate();
  153. }
  154. } isSelected={() => this._useWidthHeight} />
  155. {
  156. this._useWidthHeight &&
  157. <div className="secondLine">
  158. <NumericInputComponent label="Width" precision={0} step={1} value={this._screenShotSize.width ? this._screenShotSize.width : 512} onChange={value => this._screenShotSize.width = value} />
  159. <NumericInputComponent label="Height" precision={0} step={1} value={this._screenShotSize.height ? this._screenShotSize.height : 512} onChange={value => this._screenShotSize.height = value} />
  160. </div>
  161. }
  162. </div>
  163. </LineContainerComponent>
  164. <LineContainerComponent globalState={this.props.globalState} title="REPLAY">
  165. <ButtonLineComponent label="Generate replay code" onClick={() => this.exportReplay()} />
  166. <ButtonLineComponent label="Reset" onClick={() => this.resetReplay()} />
  167. </LineContainerComponent>
  168. <LineContainerComponent globalState={this.props.globalState} title="SCENE EXPORT">
  169. {
  170. this._isExporting &&
  171. <TextLineComponent label="Please wait..exporting" ignoreValue={true} />
  172. }
  173. {
  174. !this._isExporting &&
  175. <>
  176. <ButtonLineComponent label="Export to GLB" onClick={() => this.exportGLTF()} />
  177. <ButtonLineComponent label="Export to Babylon" onClick={() => this.exportBabylon()} />
  178. {
  179. !scene.getEngine().premultipliedAlpha && scene.environmentTexture && (scene.environmentTexture as CubeTexture).isPrefiltered && scene.activeCamera &&
  180. <ButtonLineComponent label="Generate .env texture" onClick={() => this.createEnvTexture()} />
  181. }
  182. </>
  183. }
  184. </LineContainerComponent>
  185. {
  186. (BABYLON as any).GLTFFileLoader &&
  187. <GLTFComponent scene={scene} globalState={this.props.globalState!} />
  188. }
  189. </div>
  190. );
  191. }
  192. }