Service Provider in .NET MAUI

namespace Temptor.Helpers
{
    public static class ServiceProvider
    {
        public static TService GetService<TService>()
        {
            return Current.GetService<TService>();
        }

        public static IServiceProvider Current
        {
            get 
            {
                #if WINDOWS10_0_17763_0_OR_GREATER
                    return MauiWinUIApplication.Current.Services;
                #else
                    return null;
                #endif
            }
        }
    }
}
No need to use constructor injection for any service if necessary:
namespace Temptor.Models
{
    public class Property
    {
        public PropertyService ropertyService;

        ...

        public Property() 
        {
            this.PropertyService = Helpers.ServiceProvider.GetService<PropertyService>();

            ...
        }
    }
}
Devamını Oku »

Shader Codes

 Circle Drawing:

float4 PixelShaderFunction(float2 coords: TEXCOORD0) : COLOR0
{
    float2 center = float2(0.5, 0.5);

    float distance = length(coords - center);

    float radius = 0.4;

    float4 color = (distance < radius) ? float4(0, 0, 0, 1) : float4(1, 1, 1, 1);

    return color;
}

technique Technique1
{
    pass Pass1
    {
        PixelShader = compile ps_2_0 PixelShaderFunction();
    }
}

 sin and cos

float4 PixelShaderFunction(float2 coords: TEXCOORD0) : COLOR0
{

    float amplitude = 0.9;
    float frequency =55.0;
    
    // vertical
    float sinValue = amplitude * sin(coords.x * frequency);

    // horizontal
    float cosValue = amplitude * cos(coords.y * frequency);

    float4 color = float4(sinValue, 0, cosValue, 1);

    return color;
}

technique Technique1
{
    pass Pass1
    {
        PixelShader = compile ps_2_0 PixelShaderFunction();
    }
}

 Basic Animation:

Pass the time parameter from update method with these code lines that float time = (float)gameTime.TotalGameTime.TotalMilliseconds;, and then effect.Parameters["time"].SetValue(time);.

float time;

float4 PixelShaderFunction(float2 coords: TEXCOORD0) : COLOR0
{
    float amplitude = 0.9;
    float frequency =55.0;
    
    // vertical
    float sinValue = amplitude * sin((coords.x + time * 0.5) * frequency);

    float4 color = float4(sinValue, 0, 0, 1);

    return color;
}

technique Technique1
{
    pass Pass1
    {
        PixelShader = compile ps_2_0 PixelShaderFunction();
    }
}
float4 PixelShaderFunction(float2 coords: TEXCOORD0) : COLOR0
{
    return float4(coords.x, 0.0, 0.0, 1.0);
}

technique Technique1
{
    pass Pass1
    {
        PixelShader = compile ps_2_0 PixelShaderFunction();
    }
}
float4 PixelShaderFunction(float2 coords: TEXCOORD0) : COLOR0
{
    return float4(0.0, coords.y, 0.0, 1.0);
}

technique Technique1
{
    pass Pass1
    {
        PixelShader = compile ps_2_0 PixelShaderFunction();
    }
}
float4 PixelShaderFunction(float2 coords: TEXCOORD0) : COLOR0
{
    // make (0,0) origin
    float2 uv = (coords - float2(0.5, 0.5)) * float2(2,-2);

    return float4(uv.x, uv.y, 0.0, 1.0);
}

technique Technique1
{
    pass Pass1
    {
        PixelShader = compile ps_2_0 PixelShaderFunction();
    }
}
float4 PixelShaderFunction(float2 coords: TEXCOORD0) : COLOR0
{
    // make (0,0) origin
    float2 uv = (coords - float2(0.5, 0.5)) * float2(2,-2);

    // length function calculate the magnitude of the vector. length(vec(0,0)) = 0, length(vec(0,1)) = 1    
    float distance = length(uv);

    return float4(0.0, distance, distance, 1.0);
}

technique Technique1
{
    pass Pass1
    {
        PixelShader = compile ps_2_0 PixelShaderFunction();
    }
}
float4 PixelShaderFunction(float2 coords: TEXCOORD0) : COLOR0
{
    // make (0,0) origin
    float2 uv = (coords - float2(0.5, 0.5)) * float2(2,-2);

    // length function calculate the magnitude of the vector. length(vec(0,0)) = 0, length(vec(0,1)) = 1    
    float distance = length(uv);

    distance -= 0.7;

    distance = abs(distance);

    return float4(distance, 0, distance, 1.0);
}

technique Technique1
{
    pass Pass1
    {
        PixelShader = compile ps_2_0 PixelShaderFunction();
    }
}
float4 PixelShaderFunction(float2 coords: TEXCOORD0) : COLOR0
{
    // make (0,0) origin
    float2 uv = (coords - float2(0.5, 0.5)) * float2(2,-2);

    // length function calculate the magnitude of the vector. length(vec(0,0)) = 0, length(vec(0,1)) = 1    
    float distance = length(uv);

    distance -= 0.7;

    distance = abs(distance);

    //distance = step(0.1, distance)
    distance = smoothstep(0.0, 0.2, distance);

    return float4(distance, 0, distance, 1.0);
}

technique Technique1
{
    pass Pass1
    {
        PixelShader = compile ps_2_0 PixelShaderFunction();
    }
}
float4 PixelShaderFunction(float2 coords: TEXCOORD0) : COLOR0
{
    // make (0,0) origin
    float2 uv = (coords - float2(0.5, 0.5)) * float2(2,-2);

    // length function calculate the magnitude of the vector. length(vec(0,0)) = 0, length(vec(0,1)) = 1    
    float distance = length(uv);

    distance = cos(distance * 10)/10;

    distance = abs(distance);

    //distance = step(0.1, distance)
    distance = smoothstep(0.0, 0.2, distance);

    return float4(distance, 0, distance, 1.0);
}

technique Technique1
{
    pass Pass1
    {
        PixelShader = compile ps_2_0 PixelShaderFunction();
    }
}
float time;

float4 PixelShaderFunction(float2 coords: TEXCOORD0) : COLOR0
{
    // make (0,0) origin
    float2 uv = (coords - float2(0.5, 0.5)) * float2(2,-2);


    // length function calculate the magnitude of the vector. length(vec(0,0)) = 0, length(vec(0,1)) = 1    
    float distance = length(uv);

    distance = cos(distance * 10 + (time * 0.005))/10;

    distance = abs(distance);

    //distance = step(0.1, distance)
    distance = smoothstep(0.0, 0.2, distance);

    return float4(distance, 0, distance, 1.0);
}

technique Technique1
{
    pass Pass1
    {
        PixelShader = compile ps_2_0 PixelShaderFunction();
    }
}
float time;

float4 PixelShaderFunction(float2 coords: TEXCOORD0) : COLOR0
{
    // make (0,0) origin
    float2 uv = (coords - float2(0.5, 0.5)) * float2(2,-2);
    
    uv *= 4;
    uv = frac(uv);
    uv -= 0.5;

    // length function calculate the magnitude of the vector. length(vec(0,0)) = 0, length(vec(0,1)) = 1    
    float distance = length(uv);

    distance = cos(distance * 10 + (time * 0.005)) / 10;

    distance = abs(distance);

    //distance = step(0.1, distance)
    distance = smoothstep(0.0, 0.2, distance);

    return float4(distance, 0, distance, 1.0);
}

technique Technique1
{
    pass Pass1
    {
        PixelShader = compile ps_2_0 PixelShaderFunction();
    }
}
float time;

static const float PI = 3.14159;

float2x2 rotate3d(float angle) {
    return float2x2(cos(angle), -sin(angle), sin(angle), cos(angle));
}

float4 PixelShaderFunction(float2 coords: TEXCOORD0) : COLOR0
{
    float2 p = coords * 2 - float2(1,1);

    float temp = (time * 0.005) * PI;

    p = mul(rotate3d(temp), p);

    float t = 0.075 / abs(0.7 - length(p));

    float vl = 0.2 * (sin(time) + 3.0);
    float3 temp2 = float3(t, t, t) * float3(vl, p.y * 0.8, 3.0);

    return float4(temp2, 1.0);
}

technique Technique1
{
    pass Pass1
    {
        PixelShader = compile ps_2_0 PixelShaderFunction();
    }
}

Devamını Oku »

A UI library for MonoGame - Devlog #1

I've been working on my own ui library for a while. Progress is a bit slow because I don't have much time. I can say it's more of a project I pursue for hobby purposes. 

There are some reasons why I am doing such a project:

  • I can work on projects in Monogame, so I might need some UI support.
  • I couldn't find a UI library in Monogame that truly meets my requirements. (There are some issues for ImGUI in Monogame)
And other things that I haven't thought of yet.

So far in the project, I'm progressing with two elements: Component and Panel. The components will be placed inside the panels. Yes, simply like that. I don't want it to be more complex for now. I have created elements such as panel, button, and scrollbar. It's functioning adequately in its basic form. In fact, it seems possible to achieve a responsive outcome.

An example image:


The project will not be open source for now. Additionally, my GitHub page has become quite cluttered.

In the next stage, I plan to add basic elements such as horizontal scrollbar, label, text box, grid etc.. I also need to create a suitable structure for handling and capturing events.

Let's create a todo list:
  • Label
  • TextBox
  • Handling Events
  • Horizontal Scrollbar
  • z-order (for collision ex.)
  • Grid (??)
  • ImageButton component (~)

Devamını Oku »

Viewport in Monogame

Let's making some practices about use of viewports with Monogame. In my definition, Viewport provides relative rendering area for programmers. I think, it can be useful for making user interface. I tried to create some ui library, but it failed. Let's learn how to use Monogame at first.

In Monogame, we have already a default viewport. It represents whole screen. 

(x = 0, y =  0, width = screen_width, height=screen_size)

Let's create our first viewport. I created a new project as Monogame project, and I create viewport called newViewport in Initialize method in the Game class. But also we need to store our default viewport, because we need it:


newViewport = new Viewport();
newViewport.X = 50;
newViewport.Y = 50;
newViewport.Width = 100;
newViewport.Height = 75;

defaultViewport = GraphicsDevice.Viewport;
If we want to draw on this viewport we have to activate it at first: Because the current viewport is the default viewport. We need to activate it in render method. Then we can draw some objects on newViewport:
protected override void Draw(GameTime gameTime)
{
    GraphicsDevice.Clear(Color.CornflowerBlue);



    GraphicsDevice.Viewport = newViewport;
    GraphicsDevice.Clear(Color.Red); // it will not work as expected 

    // render somethings on this viewport 


    // then back to the default viewport
    GraphicsDevice.Viewport = defaultViewport;


    base.Draw(gameTime);
}
After that, all screen filled by red. Because Clear method clear the buffers. This causes for all screen. But if you want to background color in viewport area, we can use RenderTarget2D for that. Let's create RenderTarget2D too in Initialize method:
newViewportTarget = new RenderTarget2D(
        GraphicsDevice,
        newViewport.Width,
        newViewport.Height
    );
So let's go in the render side. It can confuse your mind, or may not. However I confused. So instead of going deep, I'm going to be a little result-oriented. As a result, asking "why" questions is not allowed. Where we were? Rendertarget2D!. We will use RenderTarget2D for each viewport. RenderTarget is a new back buffer for us. So we can use Clear method finally. Firstly we will set rendertarget, we will clear the rendertarget and then render somethings on it and then change the rendertarget or set as null to return default backbuffer, and same steps will be start for them too:
protected override void Draw(GameTime gameTime)
{

    // red area
    GraphicsDevice.SetRenderTarget(newViewportTarget);
    GraphicsDevice.Clear(Color.Red);

    // background area
    GraphicsDevice.SetRenderTarget(null);
    GraphicsDevice.Clear(Color.CornflowerBlue);

    

    
    // draw rendertargets with related viewports

    GraphicsDevice.Viewport = newViewport;

    _spriteBatch.Begin();
    _spriteBatch.Draw(newViewportTarget, new Rectangle(0, 0, newViewport.Width, newViewport.Height), Color.White);
    _spriteBatch.End();
    
    // then back to the default viewport
    GraphicsDevice.Viewport = defaultViewport;

    

    base.Draw(gameTime);
}
Let's look at the result:
This is fair. In my opinion, this features are not enough to create complex, advances to create ui. But it's enough to make useful ui.
Devamını Oku »

Linear Regression

linear regression

$> dotnet add package Microsoft.ML Let's make an example using ML.Net. I'm going to use Popular Video Games 1980 - 2023 as data for training model.
We need to two models called GameData and PredictionData.
using System;
using Microsoft.ML;
using Microsoft.ML.Data;

public class GameData
{
    [LoadColumn(0)]
    public int Index { get; set; }

    [LoadColumn(1)]
    public string Title { get; set; }

    [LoadColumn(2)]
    [ColumnName("Release Date")]
    public string ReleaseDate { get; set; }

    [LoadColumn(3)]
    public string[] Team { get; set; }

    [LoadColumn(4)]
    public float Rating { get; set; }

    [LoadColumn(5)]
    [ColumnName("Times Listed")]
    public string TimesListed { get; set; }

    [LoadColumn(6)]
    [ColumnName("Number of Reviews")]
    public string NumberOfReviews { get; set; }

    [LoadColumn(7)]
    public string Genres { get; set; }

    [LoadColumn(8)]
    public string Summary { get; set; }

    [LoadColumn(9)]
    public string Reviews { get; set; }

    [LoadColumn(10)]
    public string Plays { get; set; }

    [LoadColumn(11)]
    public string Playing { get; set; }

    [LoadColumn(12)]
    public string Backlogs { get; set; }

    [LoadColumn(13)]
    public string Wishlist { get; set; }
}
Prediction model:
public class PredictionData
{
    public string Wishlist { get; set; }
}
Let's start to train our model using linear regression:
public class PredictionData
{
    public string Wishlist { get; set; }
}
Devamını Oku »

Making a Loading Screen for a game

I will handle how to make a loading screen for a game using C# and SFML.Net. I think it covers other frameworks as well. I mean, you can make it with other C# graphics library or other some Java frameworks most probably. 

What's loading screen? The loading screen shows some information of the process that are necessary data for the game. These data can be world map, and generating a world map can take a noticeable amount of time. For example, Terraria, etc. Therefore, we need to use loading screen for that moment.

Normally, when I started the game, there is a blockage because of the generating something for the game. At this moment, we can not do anything with the window. If I click the window, the program might be crushed. Also, it does not give a good impression. 

I will keep my example very simple. Let's get to work.

Firstly, I'm going to use a thread for generating operation. So the map is generated in the another thread. Also, I need a text object. This object will be drawn to show the percentage of the generating map on the screen. I need to get information from the thread I created to use in the main thread. I created a class called SharedData for it:

    class SharedData
    {
        public int Progress {get; set;} = 0;
    }
    
    class Game
    {
        ...
        
        Thread thread;
        Text Percentage { get; set; }        
        public static SharedData shared = new SharedData();
        
        public Game(...)
        {
            ...
            
            Percentage = new Text("0%", font, 100);
            Percentage.Position = new Vector2f(-25, -25);
            Percentage.FillColor = Color.Black;
            
            thread = new Thread(GenerateMap);
            thread.Start();
        }
I defined the SharedData object as static because we need to access it from another thread easily. The shared variable is actually a global variable. I created the text object and named it as Percentage. After that, I created a thread called thread, and add GenerateMap method to the this thread. I started this thread with Start method. Let's use this shared variable in the Generate method:
        public void GenerateMap()
        {
            for(int i = 0; i < 4116; i++)
            {
                
                // generate data here
                
            	Game.shared.Progress = i; 
            }
       	}
I just track how many times the loop has run like in above. We need to use this data in the main thread for the loading screen. If the thread is alive then we can show the loading screen with percentage. That's so simple:
        ...

        public void update(float dt)
        {
            if(thread.IsAlive)
            {
                float calc = ((float)shared.Progress / 4116f) * 100;
                Percentage.DisplayedString = ((int)calc).ToString() + "%";
            }
            else
            {
                // update generated data
            }
        }

        public void draw(RenderTarget target)
        {
            if(thread.IsAlive)
            {
                target.Draw(Percentage);
            }
            else
            {
                // draw generated data
            }
        }
Let's see how it works:
making the loading screen for game
Devamını Oku »

Specular Lighting

We learned about how to apply diffuse lighting and ambient via shaders in the previous post. In this post, we are going to learn how to apply specular which is the last stage of Phong reflection model. The surface that the light hits can be made brighter via specular reflection.

Let's visualize it:

Specular Lighting
In short, the intensity of specular illumination on the surface changes according to the position of the camera. If you remember from the last post, we calculates diffuse intensity via dot product, and we are going use it again for specular. Also, we are going to use reflect method in the fragment shader of the cube. This method gives us reflection vector according to the normal vector. After we can configure the power of specular lighting with the pow method. The pow method here is used to set the size of the reflection. If we give small values then this reflection size will be bigger than according to big values. You can try each number and you will get it what I mean. Also I changed something about diffuse lighting and ambiend lighting in the fragment shader.

Firstly, we need to the position of the camera for specular lighting. I created a uniform for cameraPosition. Then I transform it according to the world position, and pass it to the fragment shader:
#version 330 core

layout(location = 0) in vec3 aPos;
layout(location = 1) in vec2 aTexCoord;
layout(location = 2) in vec3 aNormal;

uniform mat4 uTransform;
uniform mat4 projection;
uniform mat4 view;

uniform vec3 lightPosition;
uniform vec3 cameraPosition;

out vec2 passTexCoord;
out vec3 passNormal;
out vec3 lightVector;
out vec3 cameraVector;

void main()
{
    gl_Position = vec4(aPos, 1.0) * uTransform * view * projection;
    passTexCoord = aTexCoord;

    passNormal = (vec4(aNormal, 0.0) * uTransform).xyz;
    lightVector = lightPosition - (vec4(aPos, 1.0) * uTransform).xyz;
    
    cameraVector = cameraPosition - (vec4(aPos, 1.0) * uTransform).xyz;
}   
This is the fragment shader. Briefly, it is explained in the comment lines.
#version 330 core

in vec2 passTexCoord;
in vec3 passNormal;
in vec3 lightVector;
in vec3 cameraVector;

out vec4 FragColor;

uniform sampler2D textureSampler;
uniform vec4 lightColor;

void main()
{
    // ambient lighting
    float ambient = 0.1;
    vec4 ambientColor = lightColor * ambient;
    
    // diffuse lighting
    vec3 normalizedNormal = normalize(passNormal);
    vec3 normalizedLightVector = normalize(lightVector);
    float calcDot = dot(normalizedNormal, normalizedLightVector);
    float brightness = max(calcDot, 0.0);
    float diffuse = brightness;
    vec4 diffuseColor = lightColor * diffuse;

    // specular lighting
    // this is instensity setting for reflection
    float reflectivity = 0.9;
    // reflection vector is opposite of the light vector so we need take it as negative
    // so we can find reflection vector according to the normal
    vec3 reflectionVector = reflect(-normalizedLightVector, normalizedNormal);
    vec3 normalizedReflectionVector = normalize(reflectionVector);
    vec3 normalizedCameraVector = normalize(cameraVector);
    float spec = dot(normalizedCameraVector, normalizedReflectionVector);
    float spec2 = max(spec, 0);
    // define size of reflection
    float specular = pow(spec2, 8);  
    vec4 specularColor = lightColor * specular * reflectivity;

    FragColor = texture(textureSampler, passTexCoord) * (ambientColor + diffuseColor + specularColor);
}
I updated the update method of the Camera class. Because we need to the camera position:
        public void Update()
        {
            ...
            
            foreach (var shader in this.shaderManager.Shaders)
            {
                GL.UseProgram(shader.Value.ShaderProgram);
                
                ...
                
                int uniformLocation_cameraPosition = GL.GetUniformLocation(shader.Value.ShaderProgram, "cameraPosition");
                GL.Uniform3(uniformLocation_cameraPosition, Position);
            }
        }
Let's check the result:
specular lighting
Let's make the power 64:
Specular lighting
The difference can be seen.
Devamını Oku »