Repository: Genesis-Embodied-AI/Genesis
File: genesis/options/surfaces.py (Python)

Code before:
        # update double sided
        if self.double_sided is None or force:
            if double_sided is not None:
                self.double_sided = double_sided

    def requires_uv(self):
        textures = (
            self.get_texture(),
            self.opacity_texture,
            self.roughness_texture,
            self.metallic_texture,
            self.normal_texture,
            self.emissive_texture,
        )
        return any(texture is not None and texture.requires_uv() for texture in textures)

    def get_rgba(self, batch=False):

        def fetch_batch(texture):
            if isinstance(texture, BatchTexture):
                return texture.textures if batch else texture.textures[:1]
            else:
                return [texture]

        color_textures = fetch_batch(self.get_texture() if self.emissive_texture is None else self.emissive_texture)
        opacity_textures = fetch_batch(self.opacity_texture)

        rgba_textures = []
        num_colors = len(color_textures)
        num_opacities = len(opacity_textures)
        num_rgba = num_colors * num_opacities // math.gcd(num_colors, num_opacities)

        for i in range(num_rgba):
            color_texture = color_textures[i % num_colors]
            opacity_texture = opacity_textures[i % num_opacities]

            if isinstance(color_texture, ColorTexture):
                if isinstance(opacity_texture, ColorTexture):
                    rgba_texture = ColorTexture(color=(*color_texture.color, *opacity_texture.color))
                elif isinstance(opacity_texture, ImageTexture) and opacity_texture.image_array is not None:
                    rgb_color = np.round(np.array(color_texture.color) * 255).astype(np.uint8)
                    rgb_array = np.full((*opacity_texture.image_array.shape[:2], 3), rgb_color, dtype=np.uint8)
                    rgba_array = np.dstack((rgb_array, opacity_texture.image_array))
                    rgba_scale = (1.0, 1.0, 1.0, *opacity_texture.image_color)
                    rgba_texture = ImageTexture(image_array=rgba_array, image_color=rgba_scale)
                else:
                    rgba_texture = ColorTexture(color=(*color_texture.color, 1.0))

            elif isinstance(color_texture, ImageTexture) and color_texture.image_array is not None:
                if isinstance(opacity_texture, ColorTexture):
                    a_color = np.round(np.array(opacity_texture.color) * 255).astype(np.uint8)

Review comment (performance):
Do not skip line after function signature if there is no docstring.

Do not define nested function `fetch_batch`. Just do a for-loop:
```python
all_textures = []
for texture in (
    self.get_texture() if self.emissive_texture is None else self.emissive_texture,
    self.opacity_texture,
):
    textures = texture.textures if isinstance(texture, BatchTexture) else [texture]
    all_textures.append(textures if batch else textures[:1])
color_textures, opacity_textures = all_textures
```

Code after:
        # update double sided
        if self.double_sided is None or force:
            if double_sided is not None:
                self.double_sided = double_sided

    def requires_uv(self):
        textures = (
            self.get_texture(),
            self.opacity_texture,
            self.roughness_texture,
            self.metallic_texture,
            self.normal_texture,
            self.emissive_texture,
        )
        return any(texture is not None and texture.requires_uv() for texture in textures)

    def get_rgba(self, batch=False):
        all_textures = []
        for texture in (
            self.get_texture() if self.emissive_texture is None else self.emissive_texture,
            self.opacity_texture,
        ):
            textures = texture.textures if isinstance(texture, BatchTexture) else [texture]
            all_textures.append(textures if batch else textures[:1])
        color_textures, opacity_textures = all_textures

        rgba_textures = []
        num_colors = len(color_textures)
        num_opacities = len(opacity_textures)
        num_rgba = num_colors * num_opacities // math.gcd(num_colors, num_opacities)

        for i in range(num_rgba):
            color_texture = color_textures[i % num_colors]
            opacity_texture = opacity_textures[i % num_opacities]

            if isinstance(color_texture, ColorTexture):
                if isinstance(opacity_texture, ColorTexture):
                    rgba_texture = ColorTexture(color=(*color_texture.color, *opacity_texture.color))
                elif isinstance(opacity_texture, ImageTexture) and opacity_texture.image_array is not None:
                    rgb_color = np.round(np.array(color_texture.color) * 255).astype(np.uint8)
                    rgb_array = np.full((*opacity_texture.image_array.shape[:2], 3), rgb_color, dtype=np.uint8)
                    rgba_array = np.dstack((rgb_array, opacity_texture.image_array))
                    rgba_scale = (1.0, 1.0, 1.0, *opacity_texture.image_color)
                    rgba_texture = ImageTexture(image_array=rgba_array, image_color=rgba_scale)
                else:
                    rgba_texture = ColorTexture(color=(*color_texture.color, 1.0))

            elif isinstance(color_texture, ImageTexture) and color_texture.image_array is not None:
                if isinstance(opacity_texture, ColorTexture):
                    a_color = np.round(np.array(opacity_texture.color) * 255).astype(np.uint8)
                    a_array = np.full((*color_texture.image_array.shape[:2],), a_color, dtype=np.uint8)