Set texture from an image

I've got a problem to set texture from an image on a sphere. The problem is that texture.Load or texture.SetData always returns false. I did try different methods like SetData, Load, resize texture and image (to a power of 2 number) and ... but none of them worked. Here is my code:

async void CreateScene()
{
Input.SubscribeToTouchEnd(OnTouched);

        _scene = new Scene();
        _octree = _scene.CreateComponent<Octree>();

        _plotNode = _scene.CreateChild();
        var baseNode = _plotNode.CreateChild().CreateChild();
        var plane = baseNode.CreateComponent<StaticModel>();
        plane.Model = CoreAssets.Models.Sphere;

        var cameraNode = _scene.CreateChild();
        _camera = cameraNode.CreateComponent<Camera>();
        cameraNode.Position = new Vector3(10, 15, 10) / 1.75f;
        cameraNode.Rotation = new Quaternion(-0.121f, 0.878f, -0.305f, -0.35f);

        Node lightNode = cameraNode.CreateChild();
        var light = lightNode.CreateComponent<Light>();
        light.LightType = LightType.Point;
        light.Range = 100;
        light.Brightness = 1.3f;

        int size = 3;
        baseNode.Scale = new Vector3(size * 1.5f, 1, size * 1.5f);

        var imageStream = await new HttpClient().GetStreamAsync("some 512 * 512 jpg image");
        var ms = new MemoryStream();
        imageStream.CopyTo(ms);

        var image = new Image();
        var isLoaded = image.Load(new MemoryBuffer(ms));
        if (!isLoaded)
        {
            throw new Exception();
        }

        var texture = new Texture2D();
        //var isTextureLoaded = texture.Load(new MemoryBuffer(ms.ToArray()));
        var isTextureLoaded = texture.SetData(image);
        if (!isTextureLoaded)
        {
            throw new Exception();
        }

        var material = new Material();
        material.SetTexture(TextureUnit.Diffuse, texture);
        material.SetTechnique(0, CoreAssets.Techniques.Diff, 0, 0);
        plane.SetMaterial(material);

        try
        {
            await _plotNode.RunActionsAsync(new EaseBackOut(new RotateBy(2f, 0, 360, 0)));
        }
        catch (OperationCanceledException) { }
    }

Please help!

Best Answers

  • lahellerlaheller US ✭✭
    Accepted Answer

    @ShahramShobeiri
    For me the below works. As an image I used this (put to my Assets/Data/Textures folder).

    using Urho;
    using Urho.Shapes;
    
    var app = SimpleApplication.Show(new ApplicationOptions("Data") { Width = 1280, Height = 800 });
    
    app.Viewport.SetClearColor(Color.Black);
    app.Renderer.MaterialQuality = 15;
    
    var sphere = app.RootNode.GetOrCreateComponent<Sphere>();
    var i = app.ResourceCache.GetImage("Textures/world.topo.bathy.200401.3x5400x2700.png");
    var m = Material.FromImage(i);
    sphere.SetMaterial(m);
    
  • lahellerlaheller US ✭✭
    Accepted Answer

    @puneetmahali
    Nothing to explain. There are use cases when storing resources (images, textures, etc) is better than manage images dynamically.

    But below is an (not optimized) example, how to download the image and use it as texture/material on the fly:

    using Urho;
    using Urho.Resources;
    using Urho.Shapes;
    using System.Net;
    using System.Text;
    
    var url = "https://eoimages.gsfc.nasa.gov/images/imagerecords/73000/73580/world.topo.bathy.200401.3x5400x2700.png";
    var wc = new WebClient() { Encoding = Encoding.UTF8 };
    
    var app = SimpleApplication.Show(new ApplicationOptions("Data") { Width = 1280, Height = 800 });
    app.Viewport.SetClearColor(Color.Black);
    app.Renderer.MaterialQuality = 15;
    
    try {
        var mb = new MemoryBuffer(wc.DownloadData(url));
        var sphere = app.RootNode.GetOrCreateComponent<Sphere>();
        var img = new Urho.Resources.Image(app.Context) { Name = "MyImage" };
        img.Load(mb);
        var m = Material.FromImage(img);
        sphere.SetMaterial(m);
    }
    catch (Exception ex) {
        // do something when an error occurs
    }
    

Answers

  • puneetmahalipuneetmahali Member ✭✭

    Hi Shahram,

    Can you please send the full project?

  • lahellerlaheller USMember ✭✭

    What about to use Material.FromImage(string) method?

  • ShahramShobeiriShahramShobeiri USMember ✭✭

    Thanks @laheller
    It didn't work either, After Material.FromImage(string) sphere disappears and everything gets black.

  • lahellerlaheller USMember ✭✭
    Accepted Answer

    @ShahramShobeiri
    For me the below works. As an image I used this (put to my Assets/Data/Textures folder).

    using Urho;
    using Urho.Shapes;
    
    var app = SimpleApplication.Show(new ApplicationOptions("Data") { Width = 1280, Height = 800 });
    
    app.Viewport.SetClearColor(Color.Black);
    app.Renderer.MaterialQuality = 15;
    
    var sphere = app.RootNode.GetOrCreateComponent<Sphere>();
    var i = app.ResourceCache.GetImage("Textures/world.topo.bathy.200401.3x5400x2700.png");
    var m = Material.FromImage(i);
    sphere.SetMaterial(m);
    
  • puneetmahalipuneetmahali Member ✭✭

    @laheller
    Can you please explain more about (put to my Assets/Data/Textures folder), because if image will be fetch from url then why we need to put the image into the folder also. Because I want to show the multiple images or we can say dynamically.

  • puneetmahalipuneetmahali Member ✭✭

    @laheller
    It would be so helpful If you share the working demo project.

  • lahellerlaheller USMember ✭✭
    Accepted Answer

    @puneetmahali
    Nothing to explain. There are use cases when storing resources (images, textures, etc) is better than manage images dynamically.

    But below is an (not optimized) example, how to download the image and use it as texture/material on the fly:

    using Urho;
    using Urho.Resources;
    using Urho.Shapes;
    using System.Net;
    using System.Text;
    
    var url = "https://eoimages.gsfc.nasa.gov/images/imagerecords/73000/73580/world.topo.bathy.200401.3x5400x2700.png";
    var wc = new WebClient() { Encoding = Encoding.UTF8 };
    
    var app = SimpleApplication.Show(new ApplicationOptions("Data") { Width = 1280, Height = 800 });
    app.Viewport.SetClearColor(Color.Black);
    app.Renderer.MaterialQuality = 15;
    
    try {
        var mb = new MemoryBuffer(wc.DownloadData(url));
        var sphere = app.RootNode.GetOrCreateComponent<Sphere>();
        var img = new Urho.Resources.Image(app.Context) { Name = "MyImage" };
        img.Load(mb);
        var m = Material.FromImage(img);
        sphere.SetMaterial(m);
    }
    catch (Exception ex) {
        // do something when an error occurs
    }
    
  • puneetmahalipuneetmahali Member ✭✭

    @laheller
    Thanks for quick reply.

    Sounds Cool.
    Is the above solution working/executable for both means iOS & Android for you?
    Because it's not working and shows the error like failed to add resource path 'Data', check the documentation. I already give the build action BundleResource for iOS & EmbededResource for Droid.

    So, It would be so helpful if you can share the executable demo.

    Thanks in advance.

  • lahellerlaheller USMember ✭✭
    edited August 2018

    @puneetmahali

    The app in my example is an instance of Urho.Application or Urho.SimpleApplication.
    You have to create your one and use the rest from my example.
    It should work on all platforms because of Xamarin :)

  • ShahramShobeiriShahramShobeiri USMember ✭✭

    Thank you @laheller, You helped a lot,
    My main goal was to create a 360 degree image viewer, I found a NuGet (Swank.FormsPlugin) for this goal but it didn't work for me, So based on Swank.FormsPlugin and @laheller solution, I created a very simple project to show 360 degree images that works!
    Here is the project for @laheller :)
    https://drive.google.com/file/d/1W64xybVTo_xzjb94E5AE_EO7eymB3Uwu/view?usp=sharing

  • puneetmahalipuneetmahali Member ✭✭

    @ShahramShobeiri
    This is not working on iOS gives the OpenGl error - "Error: Could not create OpenGL context, root cause 'failed to create OpenGL ES drawable".

  • lahellerlaheller USMember ✭✭

    @puneetmahali
    Is my second example working for you?
    BTW I never tried any Xamarin.IOS, I develop only for Windows and Android.

  • puneetmahalipuneetmahali Member ✭✭
    edited August 2018

    Actually I am working on Indoor Maps. I want to load the 2D Indoor Map images into 3D viewer with 360 degree movement. I am using Texture with Sphere shape. But It's not display into the full screen and light shade also come.

    Can you please help me to solve this problem.

    Here is my example-

        public IndoorMapPage(ApplicationOptions options) : base(options)
        {
            UnhandledException += Application_UnhandledException;
        }
    
        public IndoorMapPage(IntPtr handle) : base(handle)
        {
            UnhandledException += Application_UnhandledException;
        }
    
        protected IndoorMapPage(UrhoObjectFlag emptyFlag) : base(emptyFlag)
        {
            UnhandledException += Application_UnhandledException;
        }
    
        private void Application_UnhandledException(object sender, Urho.UnhandledExceptionEventArgs e)
        {
            e.Handled = true;
        }
    
        protected override void Start()
        {
            base.Start();
            CreateScene();
            SetupViewport();
        }
    
        async void CreateScene()
        {
            //Input.SubscribeToTouchEnd(OnTouched);
    
            _scene = new Scene();
            _octree = _scene.CreateComponent<Octree>();
    
            _plotNode = _scene.CreateChild();
            var baseNode = _plotNode.CreateChild().CreateChild();
            var plane = baseNode.CreateComponent<StaticModel>();
            plane.Model = CoreAssets.Models.Sphere;
    
            cameraNode = _scene.CreateChild();
            _camera = cameraNode.CreateComponent<Camera>();
            //cameraNode.Position = new Vector3(10, 15, 10) / 1.75f;
            //cameraNode.Rotation = new Quaternion(-0.121f, 0.878f, -0.305f, -0.35f);
    
            cameraNode.Position = new Vector3(0, 0, 0);
            //cameraNode.Rotation = new Quaternion(0, 180, 0);
            _camera.Fov = 70.0f;
    
            Node lightNode = cameraNode.CreateChild();
            var light = lightNode.CreateComponent<Light>();
            light.LightType = LightType.Directional;
            light.Range = 100;
            light.Brightness = 1f;
            light.Color = Color.White;
            light.CastShadows = false;
           // light.ShadowCascade = new CascadeParameters(10.0f, 50.00f, 200.0f, 0.0f,0.8f);
            lightNode.SetDirection(new Vector3(1f, -0.25f, 1.0f));
    
            int size = 3;
            baseNode.Scale = new Vector3(size * 1.5f, 1, size * 1.5f);
    
            // Set texture
            //var cache = ResourceCache;
            //Texture2D texture = cache.GetTexture2D("Textures/HeightMap.png");
    
            var imageStream = await new HttpClient().GetStreamAsync("http://www.ferienwohnung-hoedingen.de/bilder/anfahrt01.png");
    
            var ms = new MemoryStream();
            imageStream.CopyTo(ms);
    
            var image = new Image();
            var isLoaded = image.Load(new MemoryBuffer(ms.ToArray()));
            if (!isLoaded)
            {
                throw new Exception("This image cannot be load");
            }
    
            Texture2D texture = new Texture2D();
            texture.SetNumLevels(1);
            texture.SetSize(image.Width, image.Height, Urho.Graphics.RGBAFormat, TextureUsage.Dynamic); //or Static
            texture.SetData(0, 0, 0, image.Width, image.Height, image.DataBytes);
            var isTextureLoaded = texture.Load(new MemoryBuffer(ms));
    
            if (!isTextureLoaded)
            {
                throw new Exception("This texture cannot be load");
            }
    
            var material = new Material();
            material.SetTexture(TextureUnit.Diffuse, texture);
            material.SetTechnique(0, CoreAssets.Techniques.Diff, 0, 0);
            material.CullMode = CullMode.Cw;
            plane.SetMaterial(material);
    
            try
            {
               await _plotNode.RunActionsAsync(new EaseBackOut(new RotateBy(2f, 0, 0, 0)));
            }
            catch (OperationCanceledException) { }
        }
    
    
        void SetupViewport()
        {
            var renderer = Renderer;
            var vp = new Viewport(Context, _scene, _camera, null);
            renderer.SetViewport(0, vp);
    
        }
    
    
        protected override void OnUpdate(float timeStep)
        {
            const float moveSpeed = 20.0f;
    
            MoveCameraByTouches(timeStep);
    
            if (Input.GetKeyDown(Key.W)) cameraNode.Translate(new Vector3(0.0f, 0.0f, 1.0f) * moveSpeed * timeStep);
            if (Input.GetKeyDown(Key.S)) cameraNode.Translate(new Vector3(0.0f, 0.0f, -1.0f) * moveSpeed * timeStep);
            if (Input.GetKeyDown(Key.A)) cameraNode.Translate(new Vector3(-1.0f, 0.0f, 0.0f) * moveSpeed * timeStep);
            if (Input.GetKeyDown(Key.D)) cameraNode.Translate(new Vector3(1.0f, 0.0f, 0.0f) * moveSpeed * timeStep);
        }
    
        void MoveCameraByTouches(float timeStep)
        {
            const float touchSensitivity = 2;
            var input = Input;
            for (uint i = 0, num = input.NumTouches; i < num; ++i)
            {
                TouchState state = input.GetTouch(i);
                if (state.Delta.X != 0 || state.Delta.Y != 0)
                {
                    yaw += touchSensitivity * _camera.Fov / Graphics.Height * state.Delta.X;
                    pitch += touchSensitivity * _camera.Fov / Graphics.Height * state.Delta.Y;
                    cameraNode.Rotation = new Quaternion(pitch, yaw, 0);
                }
            }
        }
    
    }
    
  • lahellerlaheller USMember ✭✭

    @puneetmahali
    Are you working on a Windows Forms/WPF application, while you have no fullscreen?
    If yes, you have to initialize your app using ApplicationOptions class.

    Regarding to the second problem "light shade also come" please run your current app, make a screenshot and post it here, because it is not clear for me, what is the problem.

  • puneetmahalipuneetmahali Member ✭✭

    @laheller
    No, I am working on shared forms for iOS & Android only. I already used ApplicationOptions-
    public class MapPage : Urho.Application then creting a scene and all.
    Now, my texture image loads like Image1 and I want to do display like Image2.

  • lahellerlaheller USMember ✭✭

    @puneetmahali
    I am not familiar with Xamarin.iOS, never used.
    I only develop on Xamarin.Android and WinForms platforms.

    On Android if you want:
    1. Fullscreen app in general, you have to specify a theme without ActionBar for your activity, for example @android:style/Theme.Holo.Light.NoActionBar or your custom style where you disable actionbar.
    Then the whole device screen will be available for Activity layout(s) or other view.
    2. For UrhoSharp you have to create the UrhoSurface within a fullscreen layout.

    BTW I can't see your screenshots here.

  • ShahramShobeiriShahramShobeiri USMember ✭✭

    Sorry @puneetmahali, I never tried any Xamarin.IOS, but it works on Android without problem.

    One more thing, in the sample project, if you want to load image from URL it's better to use
    var mb = new MemoryBuffer(wc.DownloadData(new Uri(url)));
    because
    var mb = new MemoryBuffer(wc.DownloadData(url));
    didn't work for me.

  • @laheller
    No, I am working on shared forms for iOS & Android only. I already used ApplicationOptions-
    public class MapPage : Urho.Application then creting a scene and all.
    Now, my texture image loads like Image1 and I want to do display like Image2.

  • lahellerlaheller USMember ✭✭

    @puneetmahali
    I still cannot see your screenshots.
    Upload them somewhere and share the links here.

  • @laheller @ShahramShobeiri
    We have some Urho shapes(Urho.Shapes) like Box, Sphere, Plane, Cone, Cylinder, Dome.....etc etc.
    So I need to create a Static Component and loads the models into the above shape like below-

    var plane = baseNode.CreateComponent();
    plane.Model = CoreAssets.Models.Box;

    That box provides the 3D view but now I want to load the image in a Square like a Simple ImageView through the texture. So, Which shapes will give me that shape also needs to think about Vector3 position who returns the view in the center.

    Make it more Simple- I need to load an Image with texture in 2D view like a normal ImageView(square shape) in Xamarin.forms and It should work for both in iOS & Android.

  • lahellerlaheller USMember ✭✭

    @puneetmahali

    Still not sure, what is your goal.
    You want to display a Box shape and render different textures on its 6 surfaces?

  • @laheller
    Are you able to download the Images?
    No, I want to display the Image in a square shape and render different textures without 6 surfaces.
    Please check your personal message also.

  • lahellerlaheller USMember ✭✭

    @puneetmahali

    Then you have to:

    • create a new node,
    • add a Plane component into it,
    • then assign material to the Plane, where the material is from an image of your choice.

    Finally you have to transform (Rotate/Translate/Scale) your node to the final orientation/position/size.

Sign In or Register to comment.