Erhan Ballıeker

Xamarin.Workbook ile UrhoSharp Örnekleri Bölüm 1.

Herkese Merhaba,

Bundan önce UrhoSharp’ın temellerinden, Xamarin Workbook‘tan bahsetmiştim. Bu yazıdan önce UrhoSharp Giriş ve Xamarin Workbook Giriş yazılarımı okumanızda fayda var. Bu yazımda biraz daha yoğun görsellik içeren bir örnek yapacağım. UrhoSharp’ta Shapes, Actions ve Physics namespace’lerinden bahsedeceğim. Yeni bir workbook açıp (Console .Net Framework) UrhoSharp paketini nuget package manager’dan indiriyoruz.

Önce örneğimizde kullanacağımız namespacelerimizi workbook’umuza ekliyoruz.

using Urho;
using Urho.Shapes;
using Urho.Actions;
using Urho.Physics;

Şimdi daha önceki örneklerimizde görmüş olduğumuz SimpleApplication sınıfı yardımı ile 3D canvas’ımızı oluşturuyoruz.

 var app = SimpleApplication.Show(new ApplicationOptions{
             Width = 750,
             Height = 750
           });

Canvas’ımız oluştuktan sonra, SimpleApplication nesnesinin bizim için oluşturduğu CameraNode’unun Position ve Direction property’lerini değiştirelim.

app.CameraNode.Position = new Vector3(0, 0, -5);
app.CameraNode.SetDirection(new Vector3(0, 0, 1));

SimpleApplication’ın oluşturduğu RootNode’un Position değerinin default’da nasıl oluştuğuna bakalım.

possss

Gördüğünüz gibi RootNode (0,-2,8) koordinatlarına eklenmiş. Bunu Origine taşıyalım.

 app.RootNode.Position = new Vector3(0,0,0);

Şimdi SimpleApplication’ın oluşturduğu CameraNode ve bu node’a eklediği light’ı silip biz kendimiz oluşturalım. Light component’imi direk Scene’e tanımlamak istiyorum. Hatırlatayım Light tipi default olarak Point tanımlanırdı ama biz bunu Directional olarak tanımlayalım.

// Camera Node a ait tüm child node ları siliyoruz
app.CameraNode.RemoveAllChildren();

// directional light'ı Scene'e eklemeden önce kaldırıyoruz. Hatırlarsanız bu işlemleri workbook taki her bir hücrenin birden çok kez çalışması sebebi ile, duplicate node'lar oluşturmamak için yapıyorduk.
app.Scene.RemoveChild(app.Scene.GetChild("directionalLight"));
Node lightNode = app.Scene.CreateChild("directionalLight");

// Light Node'uma Light Componenti ekliyorum.
Light light = lightNode.CreateComponent();
light.LightType = LightType.Directional;
light.Color = new Color(0.65f, 0.65f, 0.65f);

// 3D sahnelerde genellikle ışık, oyuncunun sol omzunun üzerinden geliyormuş gibi bir yönde set edilir. Bunun için,aşağıdaki şekilde bir Vector3 tanımlıyorum.
lightNode.SetDirection(new Vector3(2, -3, 1));

// Simple Application tarafından oluşturulan Zone nesnesinin Ambient color rengini veriyorum. Bunun light componentinin Color değeri ile toplamının 1'i geçmemesine dikkat edin. Aksi halde normalden fazla parlak bir sahneniz olacaktır.
app.Zone.AmbientColor = new Color(0.35f, 0.35f, 0.35f);

Oluşan 3D canvas’da mouse ile kamerayı hareket ettirebilme özelliği vardır. Mouse’ın sol tuşuna basılı tutarak hareket ettirirseniz camera’nın oynadığını farkedeceksiniz. Ben cameranın oynamasını değil, Mouse’u hareket ettirdiğimde 3D ortamı daha iyi anlamak için RootNode nesnemi Rotate etmek istiyorum.

//Önce Mouse hareketiyle cameranın oynamaması için aşadağıki SimpleApplication nesnesinde aşağıdaki değişikliği yapıyorum
app.MoveCamera = false;

Şimdi mouse’un sol tuşuna basılı halde hareket ettiğimde, RootNode’u hareket ettirecek event handler’ı oluşturacağım. Sonrasında da bu event handler SimpleApplication objesinin Update event’ine atayacağım. Bu update metodu, UrhoSharp’ın Game Loop’unda tetiklenen event’dir. Normal bir UrhoSharp application’da OnUpdate metodunu override ederek de ilerleyebilirsiniz. Bu metot sahnedeki obje ve animasyon yoğunluğuna bağlı olarak saniyede pek çok kere çalışabilmektedir.

//Update event ine atayacağım event handler ı tanımlıyorum.
void ProcessMouseMovement(UpdateEventArgs args)
{
    //Gelen input Mouse un sol tuşu ise..
    if (app.Input.GetMouseButtonDown(MouseButton.Left))
    {
        //Mouse un X ve Y koordinatları ile 2 boyutlu bir vektör tanımlıyorum. Y değerine, mouse un Y değerinin negatifini set ediyorum, çünkü mouse u yukarı oynattığımda aşağıyı, aşağı oynattığımda yukarıyı göstermek istiyorum.
        Vector2 mouseMove = new Vector2(app.Input.MouseMove.X, -app.Input.MouseMove.Y);
        
        //Her bir piksel için 1 derece kabul ediyorum
        float angle = mouseMove.Length; 

        if (angle > 0)
        {
            //Aşağıda bir vector3 tanımlıyorum, Z her zaman 0, fakat RootNode a döndürme hareketi yaptıracağım için, X ekseninde mouse u hareket ettirdiğim Y ekseni etrafında döndürüyor olucam bu yüzden X değerine mouse hareketinin Y değerini veriyorum, aynı şekilde de Y değeri yerine mouse hareketinin negatifi X değerini veriyorum.
            Vector3 axis = new Vector3(mouseMove.Y, -mouseMove.X, 0);
            app.RootNode.Rotate(Quaternion.FromAxisAngle(axis, angle), TransformSpace.Parent);
        }
    }
}

Tekrar workbook’taki her bir hücrenin tekrar tekrar çalışabilir olmasından dolayı biraz reflection ile SimpleApplication’ın Update event’ini kaldırıyorum.

using System.Reflection;
FieldInfo field = typeof(Application).GetField("Update", BindingFlags.Instance | BindingFlags.NonPublic);
field.SetValue(app, null);

//Event i kaldırdıktan sonra tekrar Upadte event ine hazırldağım event handler set ediyorum.
app.Update += ProcessMouseMovement;

Peki, Canvas’ımızı oluşturduktan sonra oldukça hazırlık kodu yazdık. Birkaç farklı şeyi de irdelemiş olduk. Artık düz bir çizgiden fazlasını yapmaya başlayalım. Önce kod ile ekrana bir üçgen çizelim. Scene’deki 3 boyutu daha iyi algılamamız için tüm eksenler yönünde farklı renklerde çizgiler çizelim. Sonrasında da Urho.Shapes namespace’i ile neleri daha kolay yapabiliriz bir bakalım.

 //üçgenin noktalarını bir VertexBuffer.PositionNormalColor dizi olarak tanımlayalım.
VertexBuffer.PositionNormalColor[] vertices = {
 new VertexBuffer.PositionNormalColor{
 Position = new Vector3(-3,0, 5),
 Color = Color.Blue.ToUInt()
 },
 new VertexBuffer.PositionNormalColor{
 Position = new Vector3(0 ,3, 5),
 Color = Color.Yellow.ToUInt()
 },
 new VertexBuffer.PositionNormalColor{
 Position = new Vector3(3, 0, 5),
 Color = Color.Red.ToUInt()
 },
};
// Z eksenine göre 5 değerinde, yani ekrandan 5 birim uzaklıkta, Tabanı X ekseninde ve yüksekliği Y eksenin 3. birimde olacak şekilde 3 vertex tanımladım.

Şimdi VertexBuffer objemizi oluşturalım.

 var vertexBuffer = new VertexBuffer(Application.CurrentContext, false);
vertexBuffer.SetSize((uint)vertices.Length, ElementMask.Position | ElementMask.Normal | ElementMask.Color);
vertexBuffer.SetData(vertices);
//Bu sefer oluşturduğum vertex in Color değerinide kullandığım için, Position ve Normal flag leri yanında ElementMask.Color flag ini de vertexBuffer ın Size ını set ederken, eklemem gerekiyor.

Şimdi Geometry objemizi oluşturup bu üç vertex’i, üçgen olarak birleştirmek istediğimi söyleyeceğim.

 var geometry = new Geometry();
geometry.SetVertexBuffer(0, vertexBuffer);
geometry.SetDrawRange(PrimitiveType.TriangleList, 0, 0, 0, (uint)vertices.Length, true);
//SetDrawRange metodu ile bu iç noktayı TriangleList olarak birleştirmek istediğimi söyledim. Yani vertices dizimde 9 vertex im olsa idi, her bir üçlü bir üçgen oluşturacaktı.

Sırada bu geometriyi kullanacak Model objemizi oluşturmak var.

 var model = new Model();
model.NumGeometries = 1;
model.SetGeometry(0, 0, geometry);
model.BoundingBox = new BoundingBox(new Vector3(0,0,0), new Vector3(1,1,1));

Şimdi Material objemi zioluşturucağım. Bunu yaparken yine bir bitmap kullanmayıp sadece bir renkten türeyen Material nesnesi oluşturacağım.

 var material = Material.FromColor(Color.Green);

Artık bu model ve material objelerini birleştireceğim static model nesnemizi yaratmam ve bu nesneyi ekranda bir node’a eklemem gerek. Şimdi bunu yapalım. Node’u eklemeden önce silmeyi unutmayalım.

app.RootNode.RemoveChild(app.RootNode.GetChild("triangleNode"));
//GetChild metodu ilgili node u bulamazsa null dönecek ama bu RemoveChild metodu için sorun değil, işlem doğrudan ignore edilir.

//Node u oluşturuyorum.
var triangleNode = app.RootNode.CreateChild("triangleNode");

//Static Model componentimi oluşturuyorum.
var triangle = triangleNode.CreateComponent();

//model i set edelim.
triangle.Model = model;

//material ı set edelim.
triangle.SetMaterial(material);

tşranglecolor
Tüm bu işlemlerden sonra ekranda yeşil bir üçgen gördük. İstersek mouse’un sol tuşuna basılı tutarak RootNode’un rotate edip üçgenimizin etrafını gezebiliriz. Fakat bir şeye dikkat ettiniz mi? Vertex’leri tanımlarken her birine birer Color atamıştım, fakat en sonunda Material oluştururken verdiğim Yeşil renk bu değerleri ezmiş oldu. Bunu ortadan kaldırmak için yapmam gereken Material bir Technique set etme olacak.

//NoTextureVCol değerindeki NoTexture bir bitmap kullanma demek, VCol kısmı ise Vertex Color kullan demek.  
material.SetTechnique(0, CoreAssets.Techniques.NoTextureVCol, 1, 1);

//Bu kodu çalıştırdığımda vertexlerin renklerininde olaya dahil olduğunu göreceğim.Fakat yeşil rengi ortadan kaldırmak için Material'ı oluştururken renksiz bir instance oluşturmak gerek. Yukarıda Materail ı tanımlarken bu değişikliği yapıp tekrar deneyip görmenizi öneririm.Bunu yaptığınızda tamamen Kırmızı sarı ve mavi renklerinin karışımı resimdeki gibi bir üçgen göreceksiniz.

Şimdi yukarıda bahsettiğimiz gibi, tüm eksenler boyunca farklı renklerde çizgiler çizelim ki RootNode u hareket ettirdikçe 3D sahneyi daha rahat algılayabilelim. Bunun için her bir eksen yönünde -50, +50 aralığında çizgiler çizmemiz yeterli olacaktır.

//Tüm vertexleri ekleyeceğim listeyi oluştuyorum...
var lineVertices = new List();

-50, + 50 aralığında eksenleri çizelim demiştik. Şimdi bu döngüyü kurup vertex’lerimizi yukarıda tanımladığımız listeye ekleyelim ama bunun da öncesinde tüm eksenleri tanımladığım basit bir dizi oluşturalım. Sonuçta her bir eksen için, bu -50 +50 döngüsü çalışmalıdır. Döngünün sonunda elimde her bir eksen 100 tane totalde 300 adet vertices olmalı.

Vector3[] unitLineVertices = {Vector3.UnitX, Vector3.UnitY, Vector3.UnitZ};

Satırın çıktısında gördüğünüz gibi, her bir eksen bir birim vektör tanımlamak için oluşturulmuş UnitX, UnitY,  UnitZ field’larından yararlandım. Bu birazdan vector oluştururken de işimize yarayacak.

foreach(var unitVector in unitLineVertices)
{
    for(int i = -50; i < 50; i++)     
     {         
            //yeni bir vertex oluşturuyorum.        
            var lineVertex = new VertexBuffer.PositionNormalColor   
            {            
                 Position = i * unitVector, //int * birim vector = Vector3.. ör: i = 5 için Position = (1,0,0) * 5 => (5,0,0) olacak. 

            //X eksenindeysem kırmızı, Y eksenindeysem Yeşil, Z eksenindeysem mavi bir çizgi istiyorum.
            Color = unitVector == Vector3.UnitX ? Color.Red.ToUInt() : (unitVector == Vector3.UnitY ? Color.Green.ToUInt() : Color.Blue.ToUInt())
        };

        lineVertices.Add(lineVertex);
    }
}

Şimdi yukarıdaki sırayla gidelim (VertexBuffer -> Geometry -> Model -> Material -> Static Model -> Node). Bu sefer dikkat etmemiz gereken tek şey: Geometry objemizi oluştururken PrimitiveType olarak LineList kullanmak olacak.

var lineVertexBuffer = new VertexBuffer(Application.CurrentContext,false);
lineVertexBuffer.SetSize((uint)lineVertices.Count, ElementMask.Position |ElementMask.Normal |ElementMask.Color, false);
lineVertexBuffer.SetData(lineVertices.ToArray());

var lineGeometry = new Geometry();
lineGeometry.SetVertexBuffer(0, lineVertexBuffer);
lineGeometry.SetDrawRange(PrimitiveType.LineList, 0, 0, 0, (uint)lineVertices.Count, true);

var lineModel = new Model();
lineModel.NumGeometries = 1;
lineModel.SetGeometry(0, 0, lineGeometry); 
lineModel.BoundingBox = new BoundingBox(new Vector3(0,0,0), new Vector3(1,1,1));

var lineMaterial = new Material();
lineMaterial.SetTechnique(0, CoreAssets.Techniques.NoTextureVCol, 1, 1);

app.RootNode.RemoveChild(app.RootNode.GetChild("lineNode"));
var lineNode = app.RootNode.CreateChild("lineNode");

var line = lineNode.CreateComponent();
line.Model = lineModel;
line.SetMaterial(lineMaterial);

Yukarıda farklı birşey yapmadık. Bu kodu çalıştırdığımızda, istediğimiz 100 birimlik tüm eksenlerimizde farklı renklerde çizgiler çizmiş olduk. Üçgenimizin tam olarak Z eksenin +5 değerinde olduğunu daha rahat görebiliriz. Mouse ile biraz oynayarak sahnemizi gezebiliriz.

triangle2

Biraz da Urho’nun kendi Shape namespace’inden faydalanıp daha az kod ile daha fazla şey yapma vakti geldi. Şimdi origin’e bir adet küre ekleyelim. Bunun için Urho.Shapes namespace’in altındaki Sphere Component’ini kullanacağız. Sphere gibi 8 adet hazır shape mevcut. Biz bu örnekte hepsini görmeyeceğiz ancak sizin hepsini ekrana ekleyip görmenizde, el alışkanlığı kazanmanız fayda var.

//Direk olarak küremi koyacağım Node u üreterek işe başlıyorum.
app.RootNode.RemoveChild(app.RootNode.GetChild("sphereNode"));
var sphereNode = app.RootNode.CreateChild("sphereNode");

//StaticModel componenti yerine, bu component ten türeyen ama farklı özelliklere de sahip olan Sphere Componentini ekliyorum node'uma
var sphere = sphereNode.CreateComponent();
sphere.Color = Color.Blue;
//Bu componentin Color propertysi sayesinde direk renk set edebiliyorum. StaticModel Componentinde normalde böyle bir property yok. Material kullanıyordum renk bitmap vb set etmek için.

Vee bu kadar. Tam olarak orijinde, 1 birim çapında, mavi bir kürem oluştu bile.
Şimdi birde küp ekleyelim ve ona biraz animasyon katalım.

app.RootNode.RemoveChild(app.RootNode.GetChild("boxNode"));

var boxNode = app.RootNode.CreateChild("boxNode");
var box = boxNode.CreateComponent();
box.Color = Color.Red;

//boxnode un position değerini değiştirelim.
boxNode.Position = new Vector3(3,3,3);

Ekranda kırmızı bir box nesnesini görebiliriz. Şimdi biraz animasyon katalım. Animasyonu yaratırken Node nesnesinin FiniteTimeAction objesini kullanacağız. Birden çok action’ı node’a set edebiliriz.

FiniteTimeAction boxAction = 
    new RepeatForever(
        //Rotate by action ı ile rotation animasyonunu ekliyorum.
        new RotateBy(duration: 5, //Animasyonun kaç saniyede tamamlanmasını istediğimi söylüyorum.
                     deltaAngleX: 0,
                     deltaAngleY: 360,//Y ekseni etrafında 360 derece dönecek ve sürekli çalışacak bir action oluşturuyorum.
                     deltaAngleZ: 0
        )
    );

//Önce tüm action ları siliyoruz.
boxNode.RemoveAllActions();

//Sonrada action'ı node a ekliyorum.
boxNode.RunActions(boxAction);

Şimdi de, 100 X 100’lük bir yüzey (düzlem) oluşturalım. Sonra da bu yüzeyin fizik kanunları ile etkilenmesi için rigid body ve collision shape’ini tanımlayıp bu düzlemimiz gibi bir de sphere oluşturup onu da yerçekiminden etkilenip yüzeye düşecek şekilde tasarlayalım.

app.RootNode.RemoveChild(app.RootNode.GetChild("planeNode"));

var planeNode = app.RootNode.CreateChild("planeNode");
var plane = planeNode.CreateComponent();
plane.Color = Color.Green;

//100X100 lük olsun demiştik. Scale i değiştirelim.
planeNode.SetScale(100);

//Bu noktadan itibaren yeşil düzlemimiz canvasımızda belirdi.Şimdi rigidBody ve collision shape componentlerimiz tanımlayalım.
//RigidBody
var planeRigidBody = planeNode.CreateComponent();

//CollisionShape nesnesi, modelimizin diğer modeller ile etkileşim sınırlarını belirler.
var planeCollisionShape = planeNode.CreateComponent();
planeCollisionShape.SetStaticPlane(new Vector3(100,0,100), Quaternion.Identity);

Şimdi yerçekiminden etkilenecek ve yüzeye düşecek küremizi oluşturalım.

app.RootNode.RemoveChild(app.RootNode.GetChild("rigidSphereNode"));
var rigidSphereNode = app.RootNode.CreateChild("rigidSphereNode");
rigidSphereNode.SetScale(2);
rigidSphereNode.Position = new Vector3(-3,5,2);

var rigidSphere = rigidSphereNode.CreateComponent();
rigidSphere.Color = Color.Yellow;

var sphereRigidBody = rigidSphereNode.CreateComponent();
sphereRigidBody.Mass = 1;

var sphereCollisionShape = rigidSphereNode.CreateComponent();
sphereCollisionShape.SetSphere(2, Vector3.Zero, Quaternion.Identity);

Yukarıdaki kod bloğunu da çalıştırdığımızda sarı bir kürenin ekranda belirip plane üzerine düştüğünü ve rootnode’ un rotasyonu olduğu yöne doğru kaydığını görüyoruz.triangle4

Yazıda oluşturduğum workbook’u buradan indirebilirsiniz.

Bir sonraki yazımda görüşmek üzere.

UrhoSharp ve Xamarin Workbook

Herkese Merhaba,

Bir önceki yazımızda Urhosharp’ın ne olduğundan ve bu kütüphaneyi kullanmak için bilmemiz gereken temel terimlerden bahsetmiştik. Bu yazımızda da bu terimleri kullanarak, Xamarin Workbook ile bir kaç örnek yapacağız. Örnekleri Workbook ile yapmayı seçmemin sebebi, hem Xamarin Workbook u biraz tanıtmak ve ne kadar kullanışlı olduğunu göstermek, hem de kod tarafında yaptığımız en küçük değişiklikleri anında görmek, telefona deploy etmenin verdiği zaman kaybından kurtulmak.

Tamamen ücretsiz ve çok kullanışlı olan Xamarin Workbook’u hem windows hem de  Mac için buradan indirebilirsiniz. Örnekleri inceleyebilir ve geri bildirimlerde bulunabilirsiniz.

workbook

Xamarin Workbook’u bilgisayarımıza indirip kurduğumuzda yandaki şekilde gördüğümüz gibi bir ikonla karşımıza çıkıyor.Bu ikona tıklayarak Workbook u açıyoruz.

workbooktemp

Workbook u ilk açtığımızda karşımıza bir template seçim ekranı geliyor. Burada Android, iOS, WPF, Console (.Net Framework veya .Net Core )seçimi yaparak workbook u hazırlıyoruz. Ben bu örneğimizde Console (.Net Framework) seçerek ilerliyeceğim.

Artık seçimlerimizi yapıp boş bir Workbook açmış olduk. Bu ekranda yapabileceklerimizden biraz bahsedip, örneğe giriş yapacağım.

workbookfile

Sol tarafta, workbook’u oluşturduğumuz ismin altında iki adet tab mevcut. Biri NuGet packages, diğeri kodlarımızı ve notlarımızı yazacağımız workbook’umuz. Tıpkı Visual Studio dan alışık olduğumuz gibi istediğimiz Nuget Package ı projemize ekleyip, projemizde kullanabiliyoruz. Hemen orta bölümde ise, workbook’umuzun kendisi mevcut, boş bir Executable cell ile gelmiş halde duruyor.  Workbook’a iki farklı satır(cell) ekleyebiliriz. Bunlardan biri Executable C# Cell bir diğeri de Documentation Cell. Her bir hücrenin en alt sağ tarafında, 3 buton mevcut. Bu butonları kullanarak, ilgili hücreyi silebilir, yeni bir dökümantasyon yada C# executable cell ekleyebiliriz. eklediğimiz hücre eğer çalıştırılabilir C# hücresi ise, hemen sol alt tarafında bir play butonu görüyor olacağız. Bu  butona bastığımızda, o hücre çalışacak ve bize o blokta ki çıktıyı (genellikle aynı hücre içerisinde en son satırda yazdığımız kodun çıktısı) hemen hücrenin altında bize gösterecek.

Workbook ile çalışırken dikkat etmemiz gereken önemli bir konu var. yukarıda bahsettiğim gibi, her bir executable cell çalıştığında, çıktıyı bize alt tarafında gösterecek, yani her bir hücrenin geriye birşey dönmesi gerekli. Normalde, Visual Studio kullanarak bir UrhoSharp örneği yapmaya kalkışsak, kendi Application objemizi, Urho.Application sınıfında türetip, gerisini bu application sınıfına bırakırdık. Bu miras aldığımız Application objesi, bizim kendi application objemiz içerisindeki “Run” methodunu tetikleyip, uygulamamızı başlatır, ve biz bir şekilde uygulamayı kapatmadığımız sürece de durdurmazdı. “Run” methodu da blocker bir method olduğundan, Workbook’ta kullanıma uygun değil malesef. Ama tam olarak bu workbook’ta kullanımımıza sunulmuş bir nesne var. SimpleApplication nesnesi bizim için, bazı initialization işlemlerinden sonra, bize bir application nesnesi dönüyor ve bunun üzerinden işlemlerimizi yapmaya devam edebiliyoruz.Bununla da kalmıyor, bizim için bir Camera, Light ve bir RootNode u üretmiş olarak kullanımımıza sunuyor.

Bir diğer dikkat etmemiz gereken konu ise, Workbook içerisindeki her bir executable cell tekrar tekrar çalışabilir olduğundan, uygulamamızda yapacağımız bazı işlemlerin, öncesinde tersini yapmamız gerekecek. Örneğin, Scene’imize bir node eklemeden önce o node u sileceğiz, veya bir event tanımladığımızda onun öncesinde de biraz reflection kullanıp kaldırmamız gerkecek. Bu tarz küçük handikapların dışında, workbook ile çalışmaya başladığınızda, özellikle yeni bir şeyi öğrenme konusunda ne kadar işe yarar olduğunuz göreceksiniz.

urhopackage

Artık örneğimize geçebiliriz. Nuget Packages‘a sağ tıklayıp, “Add Pacakge…” butonuna basarak karşımıza gelen ekrandaki Search kısmına UrhoSharp yazıyoruz ve UrhoSharp1.5.22 paketini çift tıklayarak yada alt taraftaki Add Package butonuna basarak Workbook’umuza ekliyoruz.

Bu paketi projemize eklediğimizde, ilk executable cell imizde #r “Urho” şeklinde ilgili kütüphanenin referansının workbook a eklendiğini göreceğiz. Bir Executable daha açıp, workbook’ta kullanacağımız namespace’leri ekliyoruz. Bu örneğimizde sadece Urho namespace’i yeterli olacak. Ctrl + Enter ‘a basarak veya hücrenin altındaki play butonuna basarak ilgili hücreyi çalıştırabiliriz. Workbook bizim için yeni bir executable cell oluşturacaktır. kod yazarken bir sonraki satıra geçmek için Shift + Enter’a basabiliriz.

newapp

Namespace’imizi ekledikten sonra. Bahsettiğimiz SimpleApplication sınıfının static Show metodunu kullanarak kendimize, belirttiğimiz boyutlarda bir 3D canvas açıyoruz. bundan sonraki her işlemimizi bu canvas üzerinde göreceğiz. Static Show metodu bizden bir application options nesnesi bekliyor. Yeni ApplicationOptions oluşturup, canvasımıza istediğimiz width ve height ı verip canvas ı oluşturuyoruz.

Bu örneğimizdeki amacımız ekrana sadece basit bir çizgi çizmek =) Bunu yaparken bir önceki yazımızda bahsettiğimiz terimlerin nasıl kullanıldığını göreceğiz. Sonraki örneklerimiz daha komplike olacağından, bu terimleri kullanmaya alışmamızda fayda var.

Öncelikle, boş uzayda bir çizgi çizmek için ihtiyacım olan iki noktayı tanımlayacağım. Sonrasında bu noktaları VertexBuffer nesnesinde birleştirip, devamında Geometry, Model, Material, StaticModel, Component ve Node sınıflarını kullanarak, bu çizgimizi canvas’ta göstereceğiz.

VertexBuffer.PositionNormal[] vertices = {
    new VertexBuffer.PositionNormal{
        Position = new Vector3(0,0,0)
    },
    new VertexBuffer.PositionNormal{
        Position = new Vector3(2,0,0)
    }
};

Yukarıdaki şekilde bir Vertex dizisi oluşturduk. Hatırlayalım; Vertex oluştururken, bunu VertexBuffer sınıfında tanımlanmış public structure’ ları kullanarak yapıyoruz demiştik. Sadece Position özelliklerini tanımlayıp bir Vertices(birden çok vertex e verilen ad) oluşturdum. Bu Executable Cell i çalıştırdığımda, workbook bana hücrenin çıktısını yani iki elemanlı VertexBuffer.PositionNormal dizisini aşağıda gösteriyor.

outpar

Sırada VertexBuffer nesnemizi oluşturmak var.Aşağıdaki şekilde bir VertexBuffer objesi oluşturyorum. SetSize ve SetData metodları ile değerlerimi set ediyorum.

var vertexBuffer = new VertexBuffer(Application.CurrentContext, false){
    Shadowed = true
};
vertexBuffer.SetSize((uint)vertices.Length, ElementMask.Position | ElementMask.Normal, false);
vertexBuffer.SetData(vertices);

Şimdi bu vertexbuffer’ ın ne şekilde birleşeceklerini söyleyeceğim nesenemi yani Geometry’ imi tanımlıyorum.

var geometry = new Geometry();
geometry.SetVertexBuffer(0, vertexBuffer);
geometry.SetDrawRange(PrimitiveType.LineList, 0, 0, 0, (uint)vertices.Length, true);

Sırada Model sınıfımı tanımlamak var. oluşturduğumuz geometry nesnelerini Model nesnesi ile bir araya getirebiliyorduk. BoundingBox propertysine set ettiğimiz değerlerin bu örnek için bir önemi yok, o yüzden biri orijinde diğeri (1,1,1) noktalarında iki Vector3 tanımladım.

var model = new Model();
model.NumGeometries = 1;
model.SetGeometry(0, 0, geometry);
model.BoundingBox = new BoundingBox(new Vector3(0,0,0), new Vector3(1,1,1));

Oluşturmuş olduğum bu Model objemin görselliği ile alakalı değerleri Material nesnesi ile set ediyoruz. Bu örnekte basitçe, bir desen kullanmadan sadece bir mavi renkten oluşan Material oluşturuyorum.

var material = Material.FromColor(Color.Blue);

Sonunda ekranda göstermek istediğim modelimi oluşturmuş oldum. Bunu Simple Application’ın benim için oluşturduğu RootNode’a yeni bir node ekleyerek, ekranda gösterme kısmınıda yapıp, artık bu mavi çizgimi oluşturduğum canvas’ta görmek istiyorum. Workbook’ta her bir executable cell, tekrar tekrar çalışabileceği için, oluşturacağım node’u önce RootNode’dan kaldırıyorum ki duplicate node’larım olmasın.

app.RootNode.RemoveChild(app.RootNode.GetChild("lineNode"));
var lineNode = app.RootNode.CreateChild("lineNode");

var line = lineNode.CreateComponent();
line.Model = model;
line.SetMaterial(material);

Veee sonunda canvas’ımızda da şekilde ki gibi bir mavi çizgi görüyoruz. outputurho

Bir sonraki yazımızda, biraz daha kod ağırlıklı bir örnek yapacağız. O zamana kadar bu temel terimleri anlamakta ve en temel anlamda kullanmakta fayda olacaktır.

Bu örnekte yapmış olduğum workbook dosyasını şuradan indirebilirsiniz.

Bir sonraki yazıda görüşmek üzere…

UrhoSharp’a Giriş

Herkese Merhaba,

Bu yazımda 29 Eylül 2017 Cuma akşamı Yiğit Özaksüt  ve Cihan Yakar ile gerçekleştirdiğimiz Xamarin Istanbul Development Meetup etkinliğinde anlattığım UrhoSharp’tan bahsedeceğim.

Meetup’taki sunumumu şuradan indirebilirsiniz.

Şimdi UrhoSharp konusuna gelelim. UrhoSharp, Urho3D adıyla hayata geçmiş ve başarılı bir 3D grafik ve oyun motoru olan Cross-Platform çalışabilen ve C++ API’a sahip projenin, tam olarak Xamarin ve diğer Microsoft ortamlarında çalışabilecek C# / .NET wrappers ile yeniden yazılmış halidir.

urho1

3D konusuna daha önceden hiç bulaşmamış olanlar için öncelikle UrhoSharp’ı kullanırken fazlaca kullanacağımız birkaç  terimden bahsetmek istiyorum.

  • Vertex / Vertices
  • VertexBuffer
  • Geometry
  • Model
  • Material
  • StaticModel
  • Node
  • Light
  • Camera
  • Scene

Bunların neler olduğundan bahsedelim. Boş uzaydaki tek bir noktayı düşünün, bu noktayı UrhoSharp’ta tanımlamak için kullandığımız nesne Vertex ama kendisini tam olarak bu isim ile değil de VertexBuffer nesnesi içine tanımlanmış public structure’lar olarak göreceğiz. Birazdan detaya ineceğim ama öncesinde, boş uzaydaki bu noktanın (yani Vertex in) sahip olduğu özelliklerden bahsetmek istiyorum. Hepinizin tahmin edeceği üzere olmazsa olmaz ilk özellik bu Vertex‘in uzaydaki koordinatı (Position). Bu özelliği UrhoSharp içerisinde yine bolca kullanacak olduğumuz Vector3 nesnesi ile belirtiyoruz. Bu Vector3 nesnesi bizden x, y ve z koordinatlarını isteyen constructure’a sahiptir. Mesela basit bir Vector3 nesnesini şu şekilde tanımlayabilirsiniz:

var coordinate = new Vector3(0,0,0);

(Bu kod ile 3D uzayda, tam orijinde duran,  x, y ve z değerleri 0 olan bir Vector3 tanımlamış olduk)

Vertex‘ in önemli 3 diğer özelliği daha vardır:

  1. Normal: Vertex’in bulunduğu yüzeye dik olan vektördür. Bu özellik öncelikle bize ışık ile etkileşime girme konusunda çok fayda sağlayacak.
  2. Color: Vertex in sahip olacağı renk.
  3. Texture: Vertex in bulunduğu yüzeyi kaplayacak olan dokunun, bu vertex için olan 2 boyutlu koordinatıdır. Vector2 ile tanımlıyor olacağız.

Boş uzayda tanımadığımız bu noktaları, bir araya getirmek için kullanacağımız nesnemiz ise VertexBuffer olacaktır. Yukarıda söylediğim gibi Vertex UrhoSharp’ta programatik olarak birşeyler çizebilmemiz için gerekli olan en küçük parça ama onu bu isimle görmeyeceğiz demiştim. İşte ilgili özellikleri ile bu vertex’i, VertexBuffer altındaki public structure’lar ile şu şekilde tanımlayabilirsiniz:

var VertexBuffer.PositionNormal = new VertexBuffer.PositionNormal {…}

var VertexBuffer.PositionNormalColor = new VertexBuffer.PositionNormalColor {…}

var VertexBuffer.PositionNormalColorTextcoord = new VertexBuffer.PositionNormalColorTextcoord {…}

Position özelliği dışında, diğer özelliklerini boş geçerek bir Vertex tanımlaması yapabilirsiniz. Bir de çokça duyacağınız Vertices adından bahsedeyim. Bu aslında birden fazla Vertex için duyacağınız isimdir yani bir üçgen oluşturmak için 3 adet Vertex tanımladınız diyelim, bu arkadaşları hep beraber çağırmak için Vertices adını kullanacaksınız, bilginiz olsun.

Peki, devam edelim. Noktaları oluşturdunuz ve VertexBuffer içinde bunları topladınız, bu noktaların ne şekilde birleşeceklerini nasıl söylüyoruz?

Geometry: Bu sınıfı aynı ismiyle UrhoSharp’ta yine çokça kullanmanız gerekecek. Geometry, bir veya birden fazla VertexBuffer nesnesinin ne şekilde birleşeceklerini ve nasıl yorumlanacaklarını söylemek için kullanacağımız bir nesnedir.

Vertices hazır, VertexBuffer’lar hazır, Geometry ‘er hazır. Tüm bunlardan sonra bu Geometry nesnelerini bir araya getirip bir görsel tanımlama işine bizi yaklaştıran önemli sınıflardan biri ise Model sınıfı olacak. Model sınıfı ile bir veya birden çok geometry objesini bir araya getirip bir model yaratmış olacağız. Bunu projemize önceden tanımlanmış haliyle, “.mdl” uzantılı dosya olarak ekleyebiliriz ya da programatik olarak bunu başarabiliriz.

Material: Oluşturduğumuz bu model nesnesinin, 3 boyutlu görselliği ile alakalı bileşenlerini Material nesnesi ile tanımlıyoruz. İstersek sadece bir renk verebiliriz (ki bu durumda Vertex’i tanımlarken verdiğimiz Color değerlerini ezmiş oluruz, ama ezmemenin de yolları var tabii.) istersek de bir bitmap vb şekilde yüzeyi kaplayacak olduğumuz dokuyu verebiliriz.

Bu Model  ve Material nesnelerini biraraya getirip, nihai bir görsellik sağlamak için kullancağımız sınıf ise StaticModel component’i olacaktır. Hazır Component demişken, hemen ondan da kısaca bahsedelim. Component nesnesi ile artık hali hazırda olan modelimizi hayata geçirebiliyoruz. Hayata geçirmekten kastım, öylece duran modellerimize, eklediğimiz Component’ler sayesinde, ses yaymalarını, fizik kurallarından etkilenmelerini ve birbirleri ile etkileşime geçmeleri gibi birçok özelliği vermek.

Peki artık modellerimiz hazır, belli componentler ile belli özellikler de aldılar peki bunları ekranda nasıl gösteriyor olacağız?

Son 3 önemli terimden bahsetmek istiyorum,

  1. Camera: 3D ekranımızda Camera nesnesi kendi görüş alanında kalan herşeyi bize gösteriyor olacak. Camera objesininde tabii ki, uzayda bir Position‘ı ışık yayma şekli ve bir görüş alanı olacaktır. Bu görüş alanını FOV (Field Of View) dediğimiz özelliği ile belirteceğiz. (Default hali 45 derecedir.)
  2. Light: UrhoSharp’ ta 3 tip ışık var. Bu ışıkları sahnemizi aydınlatmak ve tüm objelerimizin ne şekilde görüneceğini belirlemek için kullanıyoruz. Tam burada şunu söylemek isterim ki 3D Grafikte Curve (eğri) yüzey diye bir tanım yoktur aslında. Eğim olarak gördüğümüz şey, ışığın geliş şekli ve o yüzeyi oluşturan Vertices’in Normal vektörleri ile yaptıkları açıların etkileşiminden başka bir şey değildir.
    1. Directional: Belli bir yönden gelen ve o doğrultuda ilerleyen sonsuz ışındır. Güneş ışını gibi düşünebilirsiniz.
    2. Point: Belli bir noktadan gelen ve tüm doğrultularda yayılan ışındır. Bir ampulün yaydığı ışın olarak düşünebilirsiniz.
    3. Spot: Belli bir noktadan gelen ve belli bir doğrultuda görülecek ışındır. El feneri gibi düşünebilirsiniz.
  3. Scene: Tüm modellerimiz ve kamera ile ışık sayesinde oluşturduğumuz sahneyi temsil etmektedir.

Bu kadar fazla terimden bahsettikten sonra örnek yapmadan olmayacaktır tabii ki. Bir sonraki yazımda, tüm anlattıklarımı içeren, güzel örnekler ile karşınızda olacağım. O zamana kadar, bu terimleri sindirmenizi, UrhoSharp ile ilgili bulduğunuz yazıları okumanızı öneririm. Birkaç yardımcı link vermek isterim:

  • Xamarin.com da buradaki linkten başlayarak ilgili tüm UrhoSharp yazılarını okumanızı öneririm. UrhoSharp’ı Xamarin.Forms ile kullanabileceğimiz gibi, tabii ki Xamarin.iOS yada Xamarin.Android ile de kullanabiliriz. Hatta Xamarin dışındaki diğer Microsoft yazılım platformalarında da rahatça kullanabilirsiniz. En nihayetinde başarılı bir  C++ API ile yazılmış kütüphanenin C# ve .NET wrapper ile devşirilmiş halini kullanıyorsunuz.
  • Bu temelleri anlamadan biraz sert kaçar ama, geçenlerde Charles Petzold abimizin yaptığı webiner ı izlemenin de ilerideki aşamalarda faydası olacaktır. Kendisi kod ile mobiusstrip yaptı, izlemeye kesinlikle değer.