How can we place loading screen while changing the texture of model in react-babylonjs?

Below is the code where we load the texture or change the texture. Whlie changeing the texture the model is disappearing and reappearing with loaded model. But I want that time frame so that I can place a loading screen and is there any predefind loading screen.

    if (image_path === "blouse_ai") {
      console.log(texture);
      let mesh = modelBlouseMesh.meshes[1];
      mesh.material.albedoTexture = new Texture(texture);
    }
    if (image_path === "skirt_ai") {
      console.log(skirtMesh, "Skirt");

      let mesh = skirtMesh.meshes[1];
      mesh.material.albedoTexture = new Texture(texture);
    }
  };```

Thank you  @carolhmj  @brianzinn  brianzinn

You can use displayLoadingUI and hideLoadingUI Engine | Babylon.js Documentation (babylonjs.com)

2 Likes

Thank you @carolhmj . Any react exapmle code will helps me a lot.

This is highly dependent of how your project is set up, but a react-y way would be to use the useEngine hook Hooks - engine / canvas / scene ⋅ Storybook (brianzinn.github.io) and then call the methods as needed.

1 Like

This is the code base. I just want to add the loading screen while the albedoTexture is applyed @carolhmj @brianzinn

import { promises as fs } from "fs";
import path from "path";

import React, { Suspense, useState, useRef, useEffect } from "react";
// import { CheckIcon, QuestionMarkCircleIcon, StarIcon } from '@heroicons/react/20/solid'
// import { RadioGroup } from '@headlessui/react'
// import { ShieldCheckIcon } from 'heroicons/react/24/outline'

import "@babylonjs/loaders";

import { Engine, Scene, Model, useBeforeRender } from "react-babylonjs";
import Header from "../components/header";

// import path from "node:path/win32";

const ai_images = [
  {
    id: 0,
    url: "https://tailwindui.com/img/ecommerce-images/product-page-01-related-product-01.jpg",
    path: "/sub_subcategory_ai/blouse.svg",
    title: "blouse_ai",
  },
  {
    id: 1,
    url: "https://tailwindui.com/img/ecommerce-images/product-page-01-related-product-01.jpg",
    path: "/sub_subcategory_ai/skirt.svg",
    title: "skirt_ai",
  },
  {
    id: 2,
    url: "https://tailwindui.com/img/ecommerce-images/product-page-01-related-product-01.jpg",
    path: "/sub_subcategory_ai/backneck.svg",
    title: "backneck_ai",
  },
  {
    id: 3,
    url: "https://tailwindui.com/img/ecommerce-images/product-page-01-related-product-01.jpg",
    path: "/sub_subcategory_ai/collar.svg",
    title: "collor_ai",
  },
  {
    id: 4,
    url: "https://tailwindui.com/img/ecommerce-images/product-page-01-related-product-01.jpg",
    path: "/sub_subcategory_ai/frontneck.svg",
    title: "frontneck_ai",
  },
  {
    id: 5,
    url: "https://tailwindui.com/img/ecommerce-images/product-page-01-related-product-01.jpg",
    path: "/sub_subcategory_ai/pants.svg",
    title: "pants_ai",
  },
  {
    id: 6,
    url: "https://tailwindui.com/img/ecommerce-images/product-page-01-related-product-01.jpg",
    path: "/sub_subcategory_ai/wrist_cuffs.svg",
    title: "wrist_cuffs_ai",
  },
];

const image = [
  {
    id: 0,
    url: "https://cdn.shopify.com/s/files/1/1150/2764/products/R-CTDP00224-HEAD_36e14a1e-e2e1-4c2a-9124-9ea26984d572.jpg?v=1671450203&width=800",
  },
  {
    id: 1,
    url: "https://cdn.shopify.com/s/files/1/1150/2764/products/R-CTDP00229-HEAD.jpg?v=1671450207&width=800",
  },
  {
    id: 2,
    url: "https://cdn.shopify.com/s/files/1/1150/2764/products/R-CTSP04500-HEAD.jpg?v=1671620489&width=800",
  },
  {
    id: 3,
    url: "https://cdn.shopify.com/s/files/1/1150/2764/products/R-CTDP00217-HEAD_3aac33e5-bcaf-447b-a878-336a95dcf812.jpg?v=1671168695&width=800",
  },
  {
    id: 4,
    url: "https://cdn.shopify.com/s/files/1/1150/2764/products/R-CTDP00219-HEAD_cbdaaaeb-afbb-41b5-b046-c2e07d449164.jpg?v=1671450199&width=800",
  },
  {
    id: 5,
    url: "https://cdn.shopify.com/s/files/1/1150/2764/products/R-CTDP00222-HEAD_2e3edd5e-9053-4cf5-a2dd-81cdc570bdb9.jpg?v=1671450202&width=800",
  },
  {
    id: 6,
    url: "https://cdn.shopify.com/s/files/1/1150/2764/products/R-TSBR02031-HEAD_96f888f4-59bb-4be5-bc97-1c5b3599a8b2.jpg?v=1672219413&width=800",
  },
  {
    id: 7,
    url: "https://cdn.shopify.com/s/files/1/1150/2764/products/R-STBR00800-HEAD_66571fad-5378-47a3-a093-afe07bc9f72b.jpg?v=1672219240&width=800",
  },
  {
    id: 8,
    url: "https://cdn.shopify.com/s/files/1/1150/2764/products/R-RYHB00758-HEAD_41c8045f-8cd2-4ab4-9931-4b61037e5452.jpg?v=1669978841&width=800",
  },
  {
    id: 9,
    url: "https://cdn.shopify.com/s/files/1/1150/2764/products/R-RYKS00064-HEAD_95fac150-60b7-4184-82c7-67ec4cfc8f2a.jpg?v=1669975109&width=600",
  },
  {
    id: 10,
    url: "https://cdn.shopify.com/s/files/1/1150/2764/products/R-RYHB00603-HEAD2_32bd39f0-7c6c-45b3-832d-a1f2946f3dc3.jpg?v=1662801693&width=600",
  },
];


const MyFallback = () => {
  const boxRef: any = useRef();

  useBeforeRender((scene) => {
    if (boxRef.current) {
      var deltaTimeInMillis = scene.getEngine().getDeltaTime();

      const rpm = 10;
      boxRef.current.rotation.x = Math.PI / 4;
      boxRef.current.rotation.y +=
        (rpm / 60) * Math.PI * 2 * (deltaTimeInMillis / 1000);
    }
  });

  return <box ref={boxRef} name="fallback" size={2} />;
};

export default function Home({
  blouse_filenames,
  skirt_filenames,
  backNeck_filenames,
}) {
  const [bodyMesh, setBodyMesh] = useState<any>();
  const [scene, setScene] = useState<any>();
  const [garment, setGarment] = useState<any>(blouse_filenames);
  const [image_path, setImagePath] = useState<any>("blouse_ai");
  const [blouse, setBlouse] = useState<any>("close_neck");
  const [skirt, setSkirt] = useState<any>("flip_skirt");
  const [backNeck, setBackNeck] = useState<any>("flip_skirt");
  const [skirtMesh, setSkirtMesh] = useState<any>();
  const [modelBlouseMesh, setModelBlouseMesh] = useState<any>();
  const [modelBackNeckMesh, setModelBackNeckMesh] = useState<any>();
  const [loading, setIsloadingModel] = useState<any>();

  // let w = window.screen.width;
  function onSceneMount(e: any) {
    const { canvas, scene } = e;
    scene.clearColor = new Color4(0.85098, 0.85098, 0.85098);
    // scene.clearColor =  new Color3(1, 0.5, 0.7);
    const engine = scene.getEngine();
    engine.displayLoadingUI();
    setScene(scene);
    canvas.style.width = "fit";
    canvas.style.height = "60vh";
  }

  const onCreatedBody = async (e: any) => {
    console.log(e, "Model2");
  };

  const changeGarmentStyles = async (e: any) => {
    console.log(e.id, "Garment");
    if (e.id == 0) {
      setGarment(blouse_filenames);
      setImagePath("blouse_ai");
    }
    if (e.id == 1) {
      setGarment(skirt_filenames);
      setImagePath("skirt_ai");
    }

    if (e.id == 2) {
      setGarment(backNeck_filenames);
      setImagePath("backneck_ai");
    }
  };

  const getSelectedGarment = async (e: any) => {
    let garment_model = e.split(".")[0];

    console.log(garment_model, ":::::");

    if (image_path === "blouse_ai") {
      setBlouse(garment_model);
    }
    if (image_path === "skirt_ai") {
      setSkirt(garment_model);
    }
    if (image_path === "backneck_ai") {
      setBackNeck(garment_model);
    }
  };

  useEffect(() => {
    if (scene) {
      scene.getEngine().runRenderLoop(() => {
        if (scene) {
          const engine = scene.getEngine();
          engine.hideLoadingUI();
          scene.render();
          scene.getEngine().resize();
        }
      });
    }
  }, [scene]);

  const onBlouseModelLoaded = async (model: any) => {

    setModelBlouseMesh(model);
  };

  const onSkirtModelLoaded = async (model: any) => {
    setSkirtMesh(model);
  };

  const onSetlectTexture = async (texture: any) => {
    if (image_path === "blouse_ai") {
      let mesh = modelBlouseMesh.meshes[1];
      console.log(mesh.material.albedoTexture, "1");
      mesh.material.albedoTexture = new Texture(texture);
      console.log(mesh.material.albedoTexture, "2");
    }
    if (image_path === "skirt_ai") {
      console.log(skirtMesh, "Skirt");

      let mesh = skirtMesh.meshes[1];
      mesh.material.albedoTexture = new Texture(texture);
    }
  };

  return (
    <div>
      <Header />
      <div className="bg-grey container mx-auto ">
        <div className="grid w-full grid-cols-12 lg:gap-x-8 py-6 md:py-8 lg:py-10">
          {/* Product details */}
          <div className="col-span-12 lg:col-span-3">
            <section aria-labelledby="garment-title">
              <div className="overflow-hidden rounded-none lg:rounded-lg bg-white shadow">
                <div className="p-6 flex flex-col">
                  <h2
                    className="text-base font-medium text-gray-900"
                    id="garment-title"
                  >
                    Select texture
                  </h2>

                  <div className="flex overflow-x-scroll gap-4">
                    <div className="flex flex-row lg:grid  gap-1  lg:grid-cols-3  md:gap-2 lg:gap-3">
                      {image &&
                        image?.map((item: any, index: any) => (
                          <div
                            key={index}
                            className="cursor-pointer w-28 lg:w-auto"
                            onClick={() => onSetlectTexture(item.url)}
                          >
                            <img
                              src={item.url}
                              alt=""
                              className="w-24 h-38 flex-shrink-0 overflow-hidden rounded-md  border-2"
                            />
                          </div>
                        ))}
                    </div>
                  </div>
                </div>
              </div>
            </section>
          </div>

          <div className="col-span-12 lg:col-span-5">
            <div className="col-xs-12 col-md-6">
              <Engine
                antialias={true}
                adaptToDeviceRatio={true}
                canvasId="sample-canvas"
              >
                <Scene onSceneMount={onSceneMount}>
                  {/* <Skybox rootUrl="https://raw.githubusercontent.com/chaitanyaGitHub1/Images/main/5570863.jpg" /> */}
                  <arcRotateCamera
                    name="arc"
                    target={new Vector3(0, 1.8, 0)}
                    alpha={Math.PI / 2}
                    beta={0.5 + Math.PI / 4}
                    // panningSensibility={54}
                    radius={5}
                    minZ={0.1}
                    wheelPrecision={50}
                    lowerRadiusLimit={3}
                    upperRadiusLimit={6}
                    upperBetaLimit={Math.PI / 2} // Lock up and down moment
                    lowerBetaLimit={Math.PI / 5}
                  />
                  <hemisphericLight
                    name="light1"
                    intensity={1}
                    direction={Vector3.Up()}
                  />

                  <Suspense fallback={<MyFallback />} key={`woman.glb`}>
                    <Model
                      rootUrl={`https://3dpoc.s3.ap-south-1.amazonaws.com/`}
                      sceneFilename={`woman.glb`}
                      scaleToDimension={0}
                      scaling={new Vector3(2.5, 2.5, 2.5)}
                      // scaling={new Vector3(1,1,1)}
                      position={new Vector3(0, 0, 0)}
                      onCreated={(e) => {
                        onCreatedBody(e);
                      }}
                      // onModelLoaded={(): void => onModelLoaded(model)}
                      name={""}
                    ></Model>
                  </Suspense>
                  <Suspense fallback={<MyFallback />} key={`${blouse}.glb`}>
                    <Model
                      rootUrl={`Blouses/`}
                      // sceneFilename={`close_neck.glb`}
                      sceneFilename={`${blouse}.glb`}
                      scaleToDimension={0}
                      scaling={new Vector3(2.5, 2.5, 2.5)}
                      // scaling={new Vector3(1,1,1)}
                      position={new Vector3(0, 0, 0)}
                      onCreated={(e) => {
                        onCreatedBody(e);
                      }}
                      onModelLoaded={(model) => {
                        onBlouseModelLoaded(model);
                      }}
                      name={""}
                    ></Model>
                  </Suspense>
                  <Suspense fallback={<MyFallback />} key={`${skirt}.glb`}>
                    <Model
                      rootUrl={`skirts/`}
                      sceneFilename={`${skirt}.glb`}
                      scaleToDimension={0}
                      scaling={new Vector3(2.5, 2.5, 2.5)}
                      // scaling={new Vector3(1,1,1)}
                      position={new Vector3(0, 0, 0)}
                      onCreated={(e) => {
                        onCreatedBody(e);
                      }}
                      onModelLoaded={(model) => {
                        onSkirtModelLoaded(model);
                      }}
                      name={""}
                    ></Model>
                  </Suspense>
                </Scene>
              </Engine>
            </div>
          </div>

          <div className="col-span-12 lg:col-span-4">
            <section aria-labelledby="garment-title">
              <div className="overflow-hidden rounded-lg bg-white shadow">
                <div className="p-6">
                  <h2
                    className="text-base font-medium text-gray-900"
                    id="garment-title"
                  >
                    Select garment styles
                  </h2>

                  <div className="flex flex-col gap-4">
                    <div className="flex justify-around py-2 px-1 h-16 shadow-md border-2 rounded-full bg-ctcolor-50">
                      {image &&
                        ai_images?.map((item: any, index: any) => (
                          <div
                            key={index}
                            className={`${"h-12 w-12  border-2 rounded-full cursor-pointer"} ${
                              item.title === image_path
                                ? "bg-blue-100"
                                : "bg-newpink-50"
                            }`}
                            onClick={() => changeGarmentStyles(item)}
                          >
                            <img src={item.path} alt="" />
                          </div>
                        ))}
                    </div>

                    <div className=" p-2 grid grid-cols-4 gap-4 h-62 overflow-y-auto">
                      {garment?.map((item: any, index: any) => (
                        <div
                          key={index}
                          className="cursor-pointer"
                          onClick={() => getSelectedGarment(item)}
                        >
                          <img
                            src={`/${image_path}/${item}`}
                            alt=""
                            className="h-34 w-24 flex-shrink-0 overflow-hidden rounded-md  border-2"
                          />
                        </div>
                      ))}
                    </div>
                  </div>
                </div>
              </div>
            </section>
          </div>
        </div>
      </div>
    </div>
  );
}

export async function getStaticProps() {
  const blouseDirectory = path.join(process.cwd(), "public/blouse_ai");
  const blouse_filenames = await fs.readdir(blouseDirectory);

  const sklirtDirectory = path.join(process.cwd(), "public/skirt_ai");
  const skirt_filenames = await fs.readdir(sklirtDirectory);

  const backNeckDirectory = path.join(process.cwd(), "public/backneck_ai");
  const backNeck_filenames = await fs.readdir(backNeckDirectory);

  // By returning { props: { posts } }, the Blog component
  // will receive `posts` as a prop at build time
  return {
    props: {
      blouse_filenames,
      skirt_filenames,
      backNeck_filenames,
    },
  };
}

I kept the react code can you please let me know where should I implement the loading screen. I mean on which basis I get to know that the texture is loading. Thank you

I believe you can assume that the texture is loading when you initiate the process. Unless it has been loaded before and you could make a check on that.
mytexture.onLoadObservable will send a callback when the texture is loaded.

Thank you @mawa This is working .

        let mesh = setSkirtMesh.meshes[1];
        var texture = new Texture(selected_Texture_url);
        texture.onLoadObservable.add(() => {
          mesh.material.albedoTexture = new Texture(selected_Texture_url);
          const engine = scene.getEngine();
          console.log("delaying for check");
          engine.hideLoadingUI();
        });
      }``
1 Like