I am currently learning OpenGL and I am trying to write simple solar system application similar to the one in tutorial, yet for some reason camera behavior is really weird and I am not sure what is causing this. I want my camera to look at the sun, but all I am getting are some really weird angles of planets or nothing at all, it might not even be pure camera problem. Can somebody tell me what exactly I am doing wrong? I would appreciate some code. If someone is willing to help and prefer to check application instead of reading code here, link is added below.
The camera here will be as close to the tutorial as possible (FPS), but I got also dragging/scrolling system instead of this.
public class Camera
{
private static float eyeX, eyeY, eyeZ;
private static float centerX, centerY, centerZ;
private const float movingSpeed = 0.3f;
private const float rotationSpeed = 0.25f;
private static double i, j, k;
public static float Height { get; set; }
public static float Slope { get; set; }
public void InitCamera()
{
eyeX = 0f;
eyeY = 15f;
eyeZ = 25f;
centerX = 0;
centerY = 2;
centerZ = 0;
Look();
}
public void Look()
{
Gl.MatrixMode(OpenGL.GL_MODELVIEW);
Gl.LoadIdentity();
Gl.LookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, 0, 1, 0);
}
public void UpdateDirVector()
{
i = -Math.Sin(((double)Slope).ToRadians());
j = Math.Sin(((double)Height).ToRadians());
k = Math.Cos(((double)Slope).ToRadians());
centerX = eyeX - (float)i;
centerY = eyeY - (float)j;
centerZ = eyeZ - (float)k;
}
public static void CenterMouse()
{
if (GlCenter == null)
return;
var pos = (Point) GlCenter;
WinApi.SetCursorPos((int)Math.Round(pos.X), (int)Math.Round(pos.Y));
}
public void Update(int pressedButton)
{
if (GlCenter == null)
return;
var pos = (Point)GlCenter;
var halfHeight = GlHeight / 2;
var halfWidth = GlWidth / 2;
var position = new Pointer();
WinApi.GetCursorPos(ref position);
var diffX = (float)pos.X - position.x;
var diffY = (float)pos.Y - position.y;
if (position.y < halfHeight)
Height -= rotationSpeed * diffY;
else if (position.y > halfHeight)
Height += rotationSpeed * -diffY;
if (position.x < halfWidth)
Slope += rotationSpeed * -diffX;
else if (position.x > halfWidth)
Slope -= rotationSpeed * diffX;
UpdateDirVector();
CenterMouse();
if (pressedButton == 1) // LPM
{
eyeX -= (float)i * movingSpeed;
eyeY -= (float)j * movingSpeed;
eyeZ -= (float)k * movingSpeed;
}
else if (pressedButton == -1) // PPM
{
eyeX += (float)i * movingSpeed;
eyeY += (float)j * movingSpeed;
eyeZ += (float)k * movingSpeed;
}
Look();
}
}
Planet.cs:
public class Planet
{
private readonly PlanetTypes _planetType;
private readonly Position _position;
private float _orbitAngle;
private readonly float _sizeRadius;
private readonly float _velocity;
private readonly string _texturePath;
private uint _list;
private float _rotationAngle;
public Planet(float radius, PlanetTypes planetType, Position position, string texturePath, bool hasMoon)
{
_sizeRadius = radius;
_planetType = planetType;
_position = position;
_orbitAngle = Rng.Next(360);
_velocity = (float)Rng.NextDouble() * 0.3f;
_texturePath = texturePath;
}
public void Create()
{
var quadric = Gl.NewQuadric();
Gl.QuadricNormals(quadric, OpenGL.GLU_SMOOTH);
Gl.QuadricTexture(quadric, (int) OpenGL.GL_TRUE);
_list = Gl.GenLists(1);
Gl.NewList(_list, OpenGL.GL_COMPILE);
Gl.PushMatrix();
Gl.Rotate(270, 1, 0, 0);
Gl.Sphere(quadric, _sizeRadius, 32, 32);
Gl.PopMatrix();
Gl.EndList();
}
public void DrawOrbit()
{
Gl.Begin(OpenGL.GL_LINE_STRIP);
for (var i = 0; i <= 360; i++)
Gl.Vertex(_position.X * (float)Math.Sin(i * Math.PI / 180), 0, _position.X * (float)Math.Cos(i * Math.PI / 180));
Gl.End();
}
public void Draw()
{
DrawOrbit();
LoadTexture($"{_texturesPath}{_texturePath}");
Gl.PushMatrix();
_orbitAngle += _velocity;
_rotationAngle += 0.6f;
Gl.Rotate(_orbitAngle, 0, 1, 0);
Gl.Translate(-_position.X, -_position.Y, -_position.Z);
Gl.Rotate(_rotationAngle, 0, 1, 0);
Gl.CallList(_list);
Gl.PopMatrix();
}
}
Sun.cs
public class Sun
{
private uint _list;
private float _rotation;
private readonly string _texturePath;
public Sun(string texturePath)
{
_texturePath = texturePath;
}
public void Create()
{
var quadratic = Gl.NewQuadric();
Gl.QuadricNormals(quadratic, OpenGL.GLU_SMOOTH);
Gl.QuadricTexture(quadratic, (int)OpenGL.GL_TRUE);
_list = Gl.GenLists(1);
Gl.NewList(_list, OpenGL.GL_COMPILE);
Gl.PushMatrix();
Gl.Rotate(90, 1, 0, 0);
Gl.Sphere(quadratic, 3, 32, 32);
Gl.PopMatrix();
Gl.EndList();
}
public void Draw()
{
LoadTexture($"{_texturesPath}{_texturePath}");
Gl.PushMatrix();
_rotation += 0.05f;
Gl.Rotate(_rotation, 0, 1, 0);
Gl.CallList(_list);
Gl.PopMatrix();
}
}
Stars.cs
public class Stars
{
private readonly List<Position> starPositions = new List<Position>();
public void CreateStars(int amount)
{
var count = 0;
while (count <= amount)
{
var p = default(Position);
p.X = Rng.Next(110) * (float)Math.Pow(-1, Rng.Next());
p.Z = Rng.Next(110) * (float)Math.Pow(-1, Rng.Next());
p.Y = Rng.Next(110) * (float)Math.Pow(-1, Rng.Next());
if (!(Math.Pow(Math.Pow(p.X, 2) + Math.Pow(p.Y, 2) + Math.Pow(p.Z, 2), 1 / 3f) > 15))
continue;
starPositions.Add(p);
count++;
}
}
public void Draw()
{
Gl.Begin(OpenGL.GL_POINTS);
Gl.Color(1, 1, 1);
Gl.PointSize(3);
foreach (var starPos in starPositions)
Gl.Vertex(starPos.X, starPos.Y, starPos.Z);
Gl.End();
}
}
SolarSystem.cs (basically a collection of everything above, enums aren't necessary, I left them cause they might be useful in the future)
public class SolarSystem
{
public static Random Rng { get; } = new Random();
private readonly Stars _stars;
private readonly Sun _sun;
private readonly List<Planet> _planets;
public SolarSystem()
{
Camera = new Camera();
_stars = new Stars();
_sun = new Sun("sun.bmp");
_planets = new List<Planet>();
}
public void CreateScene()
{
_planets.Add(new Planet(0.5f, PlanetTypes.Mercury, new Position(5, 0, 0), "mercury.bmp", false)); // tylko tutaj pliki, wszedzie indziej przeksztaĆcone na .bmp
_planets.Add(new Planet(0.7f, PlanetTypes.Venus, new Position(11, 0, 0), "venus.bmp", false));
_planets.Add(new Planet(1, PlanetTypes.Earth, new Position(15, 0, 0), "earth.bmp", true));
_planets.Add(new Planet(1, PlanetTypes.Mars, new Position(22, 0, 0), "mars.bmp", false));
_planets.Add(new Planet(1.5f, PlanetTypes.Jupiter, new Position(28, 0, 0), "jupiter.bmp", false));
_planets.Add(new Planet(1.2f, PlanetTypes.Saturn, new Position(35, 0, 0), "saturn.bmp", false));
_planets.Add(new Planet(1.2f, PlanetTypes.Uranus, new Position(41, 0, 0), "uranus.bmp", false));
_planets.Add(new Planet(1.2f, PlanetTypes.Neptune, new Position(51, 0, 0), "neptune.bmp", false));
_planets.Add(new Planet(1.2f, PlanetTypes.Pluto, new Position(60, 0, 0), "pluto.bmp", false));
_stars.CreateStars(500);
_sun.Create();
foreach (var planet in _planets)
planet.Create();
}
public Camera Camera { get; }
public void DrawScene()
{
_stars.Draw();
_sun.Draw();
foreach (var planet in _planets)
planet.Draw();
}
public enum PlanetTypes
{
Mercury,
Venus,
Earth,
Mars,
Jupiter,
Saturn,
Neptune,
Uranus,
Pluto
}
}
API (including pointer position translation, because only this way I was able to center cursor, it will be removed with dragging camera system)):
public static class WinApi
{
[DllImport("GDI32.dll")]
public static extern void SwapBuffers(uint hdc);
[DllImport("user32.dll")]
public static extern void SetCursorPos(int x, int y);
[DllImport("user32.dll")]
public static extern void GetCursorPos(ref Pointer point);
[DllImport("user32.dll")]
public static extern IntPtr GetWindowDC(IntPtr hWnd);
[StructLayout(LayoutKind.Sequential)]
private struct POINT
{
public int X;
public int Y;
}
[DllImport("User32", EntryPoint = "ClientToScreen", SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool ClientToScreen(
IntPtr hWnd,
ref POINT pt);
[EnvironmentPermission(SecurityAction.LinkDemand, Unrestricted = true)]
public static Point? TransformToScreen(
Point point,
Visual relativeTo)
{
var hwndSource = PresentationSource.FromVisual(relativeTo) as HwndSource;
if (hwndSource == null)
return null;
var root = hwndSource.RootVisual;
// Transform the point from the root to client coordinates.
var transformToRoot = relativeTo.TransformToAncestor(root);
var pointRoot = transformToRoot.Transform(point);
var m = Matrix.Identity;
var transform = VisualTreeHelper.GetTransform(root);
if (transform != null)
{
m = Matrix.Multiply(m, transform.Value);
}
var offset = VisualTreeHelper.GetOffset(root);
m.Translate(offset.X, offset.Y);
var pointClient = m.Transform(pointRoot);
pointClient = hwndSource.CompositionTarget.TransformToDevice.Transform(pointClient);
var pointClientPixels = new POINT();
pointClientPixels.X = (0 < pointClient.X)
? (int)(pointClient.X + 0.5)
: (int)(pointClient.X - 0.5);
pointClientPixels.Y = (0 < pointClient.Y)
? (int)(pointClient.Y + 0.5)
: (int)(pointClient.Y - 0.5);
var pointScreenPixels = pointClientPixels;
if (ClientToScreen(
hwndSource.Handle,
ref pointScreenPixels))
{
return new Point(
pointScreenPixels.X,
pointScreenPixels.Y);
}
return new Point();
}
}
Extensions.cs
public static class Extensions
{
public static double ToDegrees(this double radians)
{
return radians * (180.0 / Math.PI);
}
public static double ToRadians(this double degrees)
{
return Math.PI * degrees / 180.0;
}
}
MainWindow.cs
public partial class MainWindow
{
private DispatcherTimer _dispatcherTimer;
private uint _hdc;
public static string _texturesPath = #"Data\Textures\";
private SolarSystem _solarSystem;
private int _movement;
public static OpenGL Gl { get; private set; }
public static Point? GlCenter { get; private set; }
public static int GlHeight { get; private set; }
public static int GlWidth { get; private set; }
public MainWindow()
{
InitializeComponent();
Loaded += MainWindow_Loaded;
ContentRendered += MainWindow_ContentRendered;
}
private void MainWindow_ContentRendered(object sender, EventArgs e)
{
Gl = openGLControl.OpenGL;
var source = (HwndSource)PresentationSource.FromVisual(openGLControl);
var hWnd = source?.Handle;
if (hWnd != null) _hdc = (uint)hWnd;
_solarSystem = new SolarSystem();
_solarSystem.Camera.InitCamera();
float[] materialAmbient = { 0.5f, 0.5f, 0.5f, 1.0f };
float[] materialDiffuse = { 1f, 1f, 1f, 1.0f };
float[] materialShininess = { 10.0f };
float[] lightPosition = { 0f, 0f, 0f, 1.0f };
float[] lightAmbient = { 0.85f, 0.85f, 0.85f, 0.0f };
Gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_AMBIENT, lightAmbient);
Gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_POSITION, lightPosition);
Gl.Material(OpenGL.GL_FRONT_AND_BACK, OpenGL.GL_SHININESS, materialShininess);
Gl.Material(OpenGL.GL_FRONT_AND_BACK, OpenGL.GL_DIFFUSE, materialDiffuse);
Gl.Material(OpenGL.GL_FRONT_AND_BACK, OpenGL.GL_AMBIENT, materialAmbient);
Gl.Enable(OpenGL.GL_LIGHTING);
Gl.Enable(OpenGL.GL_LIGHT0);
Gl.Enable(OpenGL.GL_DEPTH_TEST);
_solarSystem.CreateScene();
Gl.ClearColor(0, 0, 0, 1);
GlCenter = WinApi.TransformToScreen(new Point(openGLControl.Width / 2, openGLControl.Height / 2), openGLControl);
GlHeight = (int)openGLControl.Height;
GlWidth = (int)openGLControl.Width;
Camera.CenterMouse();
_dispatcherTimer = new DispatcherTimer();
_dispatcherTimer.Tick += DispatcherTimer_Tick;
_dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, 1);
_dispatcherTimer.Start();
}
private void DispatcherTimer_Tick(object sender, EventArgs e)
{
Gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT);
_solarSystem.Camera.Update(_movement);
_solarSystem.DrawScene();
WinApi.SwapBuffers(_hdc);
Gl.Flush();
}
private void OpenGLControl_MouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
_movement = 1;
else
_movement = -1;
}
private void OpenGLControl_MouseUp(object sender, MouseButtonEventArgs e)
{
_movement = 0;
}
private void Window_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.C)
{
GlCenter = WinApi.TransformToScreen(new Point(openGLControl.Width / 2, openGLControl.Height / 2), openGLControl); // new Point((int)(Left), (int)(Top));
}
}
public static uint LoadTexture(string filename)
{
if (string.IsNullOrEmpty(filename))
throw new ArgumentException(filename);
Gl.Enable(OpenGL.GL_TEXTURE_2D);
var texture = new uint[1];
var id = texture[0];
Gl.GenTextures(1, texture);
Gl.BindTexture(OpenGL.GL_TEXTURE_2D, id);
var bmp = new Bitmap(filename);
var bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
Gl.TexImage2D(OpenGL.GL_TEXTURE_2D, 0, 3, bmpData.Width, bmpData.Height, 0, OpenGL.GL_BGR, OpenGL.GL_UNSIGNED_BYTE, bmpData.Scan0);
Gl.TexParameter(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_MIN_FILTER, OpenGL.GL_LINEAR);
Gl.TexParameter(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_MAG_FILTER, OpenGL.GL_LINEAR);
bmp.UnlockBits(bmpData);
return id;
}
}
Link to whole Application:
https://www.dropbox.com/sh/uhfyeayxn8l7q9y/AAA8tFda5-ZLAjTUzJcwKUm6a?dl=0
UPDATE 1:
I have changed Look() function to:
public void Look()
{
Gl.MatrixMode(OpenGL.GL_PROJECTION);
Gl.LoadIdentity();
Gl.Viewport(0, 0, GlWidth, GlHeight);
Gl.Perspective(45.0f, GlWidth / (double) GlHeight, 1, 200.0);
Gl.LookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, 0, 1, 0);
Gl.MatrixMode(OpenGL.GL_MODELVIEW);
}
And now it works.
Now I can see that my application is loading incorrect textures for some reason. I guess thats because method LoadTextures() I wrote, is using Gl.GenTextures(1, texture). (There is no Gen(Single)Texture method in sharpgl or I am getting it all wrong).
UPDATE 2:
So basically most of my textures doesn't work because they are not power of two, but from what I have read, they don't have to be anymore. SO my current question is: How to force sharpGL to display NPOT textures?
UPDATE 3:
Turns out I can load them like this, but well, they are upside down :).
_texture = new Texture();
...
Gl.Enable(OpenGL.GL_TEXTURE_2D);
_texture.Create(Gl, $"{_texturesPath}{_texturePath}");
_texture.Bind(Gl);
UPDATE 4:
I can flip texture to display it properly, but question is why this is happening?
Gl.Enable(OpenGL.GL_TEXTURE_2D);
var bmp = new Bitmap($"{_texturesPath}{_texturePath}");
bmp.RotateFlip(RotateFlipType.RotateNoneFlipY);
_texture.Create(Gl, bmp);
_texture.Bind(Gl);
UPDATE 5:
While question from Update 4 still stands, I got last question: How can I recalculate Camera so it is not limited to look up / down only to -90/90 degree?
You don't appear to be setting up your viewing frustrum correctly, and are using it in an uninitialized state. You have code for it, but it is commented out in MainWindow.xaml.cs:123.
You must setup the frustrum. At least once before drawing. It can be either perspective or orthographic.
gl.MatrixMode(OpenGL.GL_PROJECTION);
gl.LoadIdentity();
gl.Perspective(60.0f, (double)Width / (double)Height, 0.01, 100.0);
Related
I am working on custom enum popup with search and facing next problem. When clicking button to show popup list of enums I can hover over that list and select items inside, but also I can as always move mouse outside popup rect and move around. And when I hover over other GUI element it reacts to that movement (hover highlight or so happens). I remembered that default Unity's enum don't have this feature and it blocks all events outside popup's rect exclude outside mouse click to close this popup.
So... My question is: How can I implement this feature in my popup window? I look into Unity's source code and don't find anything related to this. Except one moment when they are using internal function "GUI.GrabMouseControl(id);"
There are this internal function for enum button where they block mouse control somehow:
// A button that returns true on mouse down - like a popup button
internal static bool DropdownButton(int id, Rect position, GUIContent content, GUIStyle style)
{
Event evt = Event.current;
switch (evt.type)
{
case EventType.Repaint:
var hovered = position.Contains(Event.current.mousePosition);
if (showMixedValue)
{
BeginHandleMixedValueContentColor();
style.Draw(position, s_MixedValueContent, id, false, hovered);
EndHandleMixedValueContentColor();
}
else
style.Draw(position, content, id, false, hovered);
break;
case EventType.MouseDown:
if (GUIUtility.HitTest(position, evt) && evt.button == 0)
{
GUI.GrabMouseControl(id);
Event.current.Use();
return true;
}
break;
case EventType.MouseUp:
if (GUI.HasMouseControl(id))
{
GUI.ReleaseMouseControl();
Event.current.Use();
}
break;
case EventType.KeyDown:
if (GUIUtility.keyboardControl == id && evt.character == ' ')
{
Event.current.Use();
return true;
}
break;
}
return false;
}
I think that may be that magic thing to block mouse hovering. Also I think that GUIUtility.hotControll is second canditate to solve my issue. But I dont know how properly use it in my OnGUI() method. I am trying to it and get not correct result,
but I blocked hovering outside popup's rect. But it always spam console with these logs:
Should not grab hot control with an active capture. So I dropped this idea with my implementation, cause of these logs...
There are my code of popup window content:
using System;
using UnityEditor;
using UnityEngine;
public class SearchablePopup : PopupWindowContent
{
#region Popup Properties
private const float SymbolVerticalOffset = 0.75f;
private const int ClearButtonSize = 12;
private const string SearchControl = "searchablePopup_ctrl";
#endregion
public static void Show(Rect from, Type type, int current,
Action<int> onSelectionMade, int elementsToShow, int elementHeight,
int searchHeight, Texture backgroundTexture,
GUIStyle searchStyle, GUIStyle clearButtonStyle, GUIStyle elementStyle,
Color hover, Color selected, Color marker)
{
if (type.IsEnum is false) return;
var window = new SearchablePopup(type, current, onSelectionMade,
(int)from.width, elementsToShow, elementHeight, searchHeight,
backgroundTexture, searchStyle, clearButtonStyle, elementStyle,
hover, selected, marker);
PopupWindow.Show(from, window);
}
private readonly FilteredList _filteredList;
private readonly Action<int> _onSelectionMade;
private readonly int _width;
private readonly int _elementsToShow;
private readonly int _rowHeight;
private readonly int _searchHeight;
private readonly float _clearButtonPadding;
private readonly bool _clearButtonValid;
private readonly GUIStyle _searchStyle;
private readonly GUIStyle _searchTooltipStyle;
private readonly GUIStyle _clearButtonStyle;
private readonly GUIStyle _elementStyle;
private readonly Texture _backgroundTexture;
private readonly Texture _hoverTexture;
private readonly Texture _selectedTexture;
private readonly Texture _selectedCircleTexture;
private int _current;
private int _hover;
private int _keyboardIndex;
private int _scroll;
private int _controlHash = "SearchablePopup".GetHashCode();
private Vector2 _scrollPosition;
private Vector2 _clickPosition;
private bool _clickedThisFrame;
public SearchablePopup(Type enumType, int current, Action<int> onSelectionMade,
int width, int elementsToShow, int rowHeight, int searchHeight, Texture backgroundTexture,
GUIStyle searchStyle, GUIStyle clearButtonStyle, GUIStyle elementStyle,
Color hover, Color selected, Color marker)
{
_filteredList = new FilteredList(enumType, current);
_hoverTexture = SmallTexture(hover);
_selectedTexture = SmallTexture(selected);
_selectedCircleTexture = DrawCircle(4, marker);
_current = current;
_hover = _current;
_scroll = _current;
_keyboardIndex = _filteredList.CurrentIndex;
_onSelectionMade = onSelectionMade;
_elementsToShow = elementsToShow;
_width = width;
_rowHeight = rowHeight;
_searchHeight = searchHeight;
_backgroundTexture = backgroundTexture;
_searchStyle = searchStyle;
_clearButtonValid = clearButtonStyle != null;
_clearButtonStyle = clearButtonStyle;
_clearButtonPadding = _clearButtonValid ? clearButtonStyle.padding.left : 4;
_elementStyle = elementStyle;
if (_clearButtonValid is false)
{
_elementStyle = new GUIStyle(_elementStyle);
_elementStyle.alignment = TextAnchor.MiddleLeft;
}
_searchTooltipStyle = new GUIStyle(_searchStyle);
_searchTooltipStyle.normal.textColor *= 0.75f;
_searchTooltipStyle.hover.textColor = _searchTooltipStyle.normal.textColor;
_searchTooltipStyle.fontSize = (int)(_searchTooltipStyle.fontSize * 0.75f);
}
public override Vector2 GetWindowSize()
{
float height = Mathf.Min(
_filteredList.Entries.Count, _elementsToShow) * _rowHeight
+ _searchHeight;
//Strange hot-fix for Unity's Scrollview:
//We are increasing total height of our window's rect
//to be a litte bigger than actual content because
//if Scrollview's height >= Window's height than scrollbar is displayed
//what we don't want to see if our current count of entires is less than show amount
if (_filteredList.Entries.Count < _elementsToShow)
height += 0.25f;
return new Vector2(_width, height);
}
public override void OnOpen()
{
base.OnOpen();
EditorApplication.update += Repaint;
}
public override void OnClose()
{
base.OnClose();
EditorApplication.update -= Repaint;
}
private void Repaint()
{
EditorWindow.focusedWindow.Repaint();
}
public override void OnGUI(Rect rect)
{
GUI.DrawTexture(rect, _backgroundTexture);
if (Event.current.type == EventType.MouseDown)
{
_clickedThisFrame = true;
_clickPosition = Event.current.mousePosition;
}
Rect search = new Rect(0, 0, rect.width, _searchHeight);
Rect clear = Rect.MinMaxRect(search.xMax - ClearButtonSize - _clearButtonPadding,
search.center.y - ClearButtonSize * 0.5f, rect.xMax - _clearButtonPadding,
search.center.y + ClearButtonSize * 0.5f);
Rect scroll = Rect.MinMaxRect(0, search.yMax, rect.xMax, rect.yMax);
HandleKeyboardInput();
DrawSearch(search, clear);
DrawScroll(scroll);
_clickedThisFrame = false;
}
private void DrawSearch(Rect search, Rect clear)
{
GUI.FocusControl(SearchControl);
GUI.SetNextControlName(SearchControl);
var filter = TextfieldWithTooltip(search, _filteredList.Filter,
"Type to search...", _searchStyle, _searchTooltipStyle);
if (_filteredList.Refresh(filter))
{
_scrollPosition = Vector2.zero;
}
if (string.IsNullOrEmpty(_filteredList.Filter) is false)
{
if (_clearButtonValid)
{
GUI.Box(clear, GUIContent.none, _clearButtonStyle);
}
else
{
GUI.Box(clear, "x", GUI.skin.label);
}
if (_clickedThisFrame && clear.Contains(_clickPosition))
{
_filteredList.Refresh("");
_scrollPosition = Vector2.zero;
}
}
}
private void DrawScroll(Rect scroll)
{
Rect contentRect = new Rect(0, 0,
scroll.width - GUI.skin.verticalScrollbar.fixedWidth,
_filteredList.Entries.Count * _rowHeight);
_scrollPosition = GUI.BeginScrollView(scroll, _scrollPosition, contentRect);
Rect element = new Rect(0, 0, scroll.width, _rowHeight);
var eventType = Event.current.type;
for (int i = 0; i < _filteredList.Entries.Count; i++)
{
if (_scroll == _filteredList.Entries[i].Value && (eventType == EventType.Repaint || eventType == EventType.Layout))
{
GUI.ScrollTo(element);
_scrollPosition.x = 0;
_scroll = -1;
}
if (element.Contains(Event.current.mousePosition))
{
if (Event.current.type == EventType.MouseMove ||
Event.current.type == EventType.ScrollWheel)
{
_hover = _filteredList.Entries[i].Value;
_keyboardIndex = i;
}
if (Event.current.type == EventType.MouseDown)
{
_onSelectionMade(_filteredList.Entries[i].Value);
EditorWindow.focusedWindow.Close();
}
}
DrawElement(element, _filteredList.Entries[i].Value, i);
element.y = element.yMax;
}
GUI.EndScrollView();
}
private void DrawElement(Rect rect, int value, int index)
{
if (value == _current)
{
DrawBox(rect, _selectedTexture, _selectedCircleTexture);
rect.xMin += _selectedCircleTexture.width * 2f;
}
else if (value == _hover)
{
DrawBox(rect, _hoverTexture);
}
GUI.Label(rect, _filteredList.Entries[index].Content, _elementStyle);
}
private void DrawBox(Rect rect, Texture texture, Texture symbol = null)
{
GUI.DrawTexture(rect, texture);
if (symbol == null) return;
rect.xMin += symbol.width * 0.5f;
rect.y = rect.center.y - symbol.height * 0.5f + SymbolVerticalOffset;
rect.width = symbol.width;
rect.height = symbol.height;
GUI.DrawTexture(rect, symbol);
}
private void HandleKeyboardInput()
{
if (Event.current.isKey is false || Event.current.type != EventType.KeyDown) return;
var keyCode = Event.current.keyCode;
if (keyCode == KeyCode.Escape)
{
EditorWindow.focusedWindow.Close();
return;
}
if (keyCode == KeyCode.Return)
{
_onSelectionMade(_filteredList.Entries[_keyboardIndex].Value);
EditorWindow.focusedWindow.Close();
return;
}
if (keyCode == KeyCode.DownArrow)
{
_keyboardIndex = Mathf.Min(_filteredList.Entries.Count - 1, _keyboardIndex + 1);
_hover = _filteredList.Entries[_keyboardIndex].Value;
Event.current.Use();
_scroll = _hover;
}
else if (keyCode == KeyCode.UpArrow)
{
_keyboardIndex = Mathf.Max(0, _keyboardIndex - 1);
_hover = _filteredList.Entries[_keyboardIndex].Value;
GUIUtility.hotControl = 0;
Event.current.Use();
_scroll = _hover;
}
}
#region Helpers
public static Rect Shrink(Rect rect, float amount)
{
return new Rect(rect.x + amount, rect.y + amount, rect.width - amount * 2, rect.height - amount * 2);
}
public static Texture SmallTexture(string hex)
{
Texture2D texture = new Texture2D(4, 4);
var color = HexToRGB(hex);
for (int i = 0; i < texture.width; i++)
{
for (int j = 0; j < texture.height; j++)
{
texture.SetPixel(i, j, color);
}
}
texture.Apply();
return texture;
}
public static Texture SmallTexture(Color color)
{
Texture2D texture = new Texture2D(4, 4);
for (int i = 0; i < texture.width; i++)
{
for (int j = 0; j < texture.height; j++)
{
texture.SetPixel(i, j, color);
}
}
texture.Apply();
return texture;
}
public static Texture DrawCircle(int radius, string hex)
{
Texture2D texture = new Texture2D(radius * 2, radius * 2);
var color = HexToRGB(hex);
float cX = radius;
float cY = radius;
float sizeSquared = radius * radius;
for (int i = 0; i < texture.width; i++)
{
for (int j = 0; j < texture.height; j++)
{
if ((cX - i) * (cX - i) + (cY - j) * (cY - j) < sizeSquared)
{
texture.SetPixel(i, j, color);
}
else texture.SetPixel(i, j, Color.clear);
}
}
texture.Apply();
return texture;
}
public static Texture DrawCircle(int radius, Color color)
{
Texture2D texture = new Texture2D(radius * 2, radius * 2);
float cX = radius;
float cY = radius;
float sizeSquared = radius * radius;
for (int i = 0; i < texture.width; i++)
{
for (int j = 0; j < texture.height; j++)
{
if ((cX - i) * (cX - i) + (cY - j) * (cY - j) < sizeSquared)
{
texture.SetPixel(i, j, color);
}
else texture.SetPixel(i, j, Color.clear);
}
}
texture.Apply();
return texture;
}
public static Color HexToRGB(string hex)
{
hex = hex.TrimStart('#');
if (hex.Length != 6) return Color.black;
int r = Int16.Parse(hex.Substring(0, 2),
System.Globalization.NumberStyles.AllowHexSpecifier);
int g = Int16.Parse(hex.Substring(2, 2),
System.Globalization.NumberStyles.AllowHexSpecifier);
int b = Int16.Parse(hex.Substring(4, 2),
System.Globalization.NumberStyles.AllowHexSpecifier);
return new Color(r / 255f, g / 255f, b / 255f);
}
public static string TextfieldWithTooltip(Rect rect, string text, string tooltip,
GUIStyle textfieldStyle, GUIStyle tooltipStyle)
{
text = GUI.TextField(rect, text, textfieldStyle);
if (text.Length == 0)
GUI.Label(rect, tooltip, tooltipStyle);
return text;
}
#endregion
}
I will be realy glad to see some advices or straight anwsers on this problem is someone can solve it :)
Thank you!
I am learning to program a game engine which is why I followed a tutorial, with that tutorial I have gotten this far and even though my code is identical to theirs (theirs did work in the videos) its not working the way it is meant to. The triangle stays black no matter what. There is not any errors.
Main Program Script:
using System;
using OpenTK.Mathematics;
using OpenTK.Windowing.Desktop;
using OpenTK.Windowing.Common;
using System.Drawing;
using OpenTK.Graphics.OpenGL4;
using System.IO;
namespace Game_Engine
{
public static class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
GameWindowSettings gws = GameWindowSettings.Default;
NativeWindowSettings nws = NativeWindowSettings.Default;
gws.IsMultiThreaded = false;
gws.RenderFrequency = 60;
gws.UpdateFrequency = 60;
nws.APIVersion = Version.Parse("4.1.0");
nws.AutoLoadBindings = true;
nws.Size = new Vector2i(1280, 720);
nws.Title = "Horizon";
GameWindow window = new GameWindow(gws, nws);
window.UpdateFrame += (FrameEventArgs args) => {
};
ShaderProgram shaderProgram = new ShaderProgram(){id = 0};
window.Load += () =>
{
Console.WriteLine("Hello");
ShaderProgram shaderProgram = LoadShaderProgram("../../../../vertex_shader.glsl", "../../../../fragment_shader.glsl");
};
window.RenderFrame += (FrameEventArgs args) =>
{
GL.UseProgram( shaderProgram.id );
GL.ClearColor(1.0f, 0.0f, 0.0f, 0.0f);
GL.Clear(ClearBufferMask.ColorBufferBit);
float[] verts = { -0.5f, -0.5f, 0.0f, 0.5f, -0.5f, 0.0f, 0.0f, 0.5f, 0.0f };
float[] color = { 1f, 0, 0, 0, 1f ,0 ,0, 0, 1f };
int vao = GL.GenVertexArray();
int vertices = GL.GenBuffer();
int colors = GL.GenBuffer();
GL.BindVertexArray(vao);
GL.BindBuffer(BufferTarget.ArrayBuffer, vertices);
GL.BufferData( BufferTarget.ArrayBuffer, verts.Length * sizeof(float), verts, BufferUsageHint.StaticCopy);
GL.EnableVertexAttribArray( 0 );
GL.VertexAttribPointer( 0, 3, VertexAttribPointerType.Float, false, 0, 0 );
GL.BindBuffer(BufferTarget.ArrayBuffer, colors);
GL.BufferData(BufferTarget.ArrayBuffer, color.Length * sizeof(float), color, BufferUsageHint.StaticCopy);
GL.EnableVertexAttribArray(1);
GL.VertexAttribPointer(1, 3, VertexAttribPointerType.Float, false, 0, 0);
GL.DrawArrays(PrimitiveType.Triangles, 0, 3);
GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
GL.BindVertexArray(0);
GL.DeleteBuffer(vertices);
GL.DeleteBuffer(colors);
GL.DeleteVertexArray( vao );
window.SwapBuffers();
};
window.Run();
}
private static Shader LoadShader(string shaderLocation, ShaderType type)
{
int shaderId = GL.CreateShader( type );
GL.ShaderSource( shaderId, File.ReadAllText( shaderLocation ) );
GL.CompileShader( shaderId );
string infoLog = GL.GetShaderInfoLog( shaderId );
if (!string.IsNullOrEmpty(infoLog))
{
throw new Exception(infoLog);
}
return new Shader() { id = shaderId };
}
private static ShaderProgram LoadShaderProgram( string vertextShaderLocation, string fragmentShaderLocation)
{
int shaderProgramId = GL.CreateProgram();
Shader vertextShader = LoadShader(vertextShaderLocation, ShaderType.VertexShader);
Shader fragmentShader = LoadShader(fragmentShaderLocation, ShaderType.FragmentShader);
GL.AttachShader(shaderProgramId, vertextShader.id);
GL.AttachShader(shaderProgramId, fragmentShader.id);
GL.LinkProgram(shaderProgramId);
GL.DetachShader(shaderProgramId, vertextShader.id);
GL.DetachShader(shaderProgramId, fragmentShader.id);
GL.DeleteShader(vertextShader.id);
GL.DeleteShader(fragmentShader.id);
string infoLog = GL.GetProgramInfoLog(shaderProgramId);
if (!string.IsNullOrEmpty(infoLog))
{
throw new Exception(infoLog);
}
return new ShaderProgram() { id = shaderProgramId };
}
public struct Shader
{
public int id;
}
public struct ShaderProgram
{
public int id;
}
}
}
Fragment Shader (in glsl):
#version 400
in vec3 color_in;
out vec4 color_out;
void main(){
color_out = vec4(color_in.r, color_in.g, color_in.b, 1);
}
VertexShader (in glsl):
#version 330
layout(location = 0) in vec3 vPosition;
layout(location = 1) in vec3 vColors;
out vec3 color_in;
void main() {
color_in = vColors;
gl_Position = vec4( vPosition, 1.0 );
}
I have tried everything I could with my very limited knowledge of OpenTK and nothing has changed. I have searched on the web and for answer they still have not helped
You actually assign the shader program to a local variable in the event callback function's scope. You need to assign it to the variable in scope of Main:
ShaderProgram shaderProgram = new ShaderProgram() { id = 0 };
window.Load += () =>
{
Console.WriteLine("Hello");
// ShaderProgram shaderProgram = LoadShaderProgram("../../../../vertex_shader.glsl", "../../../../fragment_shader.glsl");
shaderProgram = LoadShaderProgram("../../../../vertex_shader.glsl", "../../../../fragment_shader.glsl");
};
Im trying to follow this tut here http://neokabuto.blogspot.com/2014/07/opentk-tutorial-6-part-3-putting-it-all.html (backup http://web.archive.org/web/20190125230120/http://neokabuto.blogspot.com/2014/07/opentk-tutorial-6-part-3-putting-it-all.html) But I have been threw the code like a hundred times but I still keep getting black cubes :(.
Heres a download link for my hole project https://mega.nz/folder/t8NAxDRJ#MEIlwc96FZEPi_pUuB2xaA
heres what it looks like
Heres my main script, maby the errors in that?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using OpenTK;
//using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
using System.IO;
using OpenTK.Input;
using System.Windows.Media.Imaging;
using System.Drawing.Imaging;
//using System.Windows.Input;
namespace OpenTKTutorial1
{
class Game: GameWindow
{
public Game() : base(1000,500, new OpenTK.Graphics.GraphicsMode(32,24,0,4))
{
}
//Just like we had a Dictionary to allow us to use strings to
//organize our shaders, we'll use another to keep track of our
//texture IDs. Add this to the Game class:
Dictionary<string, int> textures = new Dictionary<string, int>();
Vector2[] texcoorddata;
Camera cam;
Vector2 lastMousePos = new Vector2();
float time = 0.0f;
int ibo_elements;
Dictionary<string, ShaderProgram> shaders = new Dictionary<string, ShaderProgram>();
string activeShader = "default";
Vector3[] vertdata;
Vector3[] coldata;
List<Volume> objects = new List<Volume>();
int[] indicedata;
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
cam = new Camera();
initProgram();
Title = "Hello OpenTK!";
GL.ClearColor(Color.CornflowerBlue);
GL.PointSize(5f);
}
void initProgram()
{
lastMousePos = new Vector2(Mouse.X, Mouse.Y);
GL.GenBuffers(1, out ibo_elements);
shaders.Add("default", new ShaderProgram("Shaders/vs.glsl", "Shaders/fs.glsl", true));
shaders.Add("textured", new ShaderProgram("Shaders/vs_tex.glsl", "Shaders/fs_tex.glsl", true));
activeShader = "textured";
textures.Add("pictures/opentksquare.png", loadImage("pictures/opentksquare.png"));
textures.Add("pictures/opentksquare2.png", loadImage("pictures/opentksquare2.png"));
TexturedCube tc = new TexturedCube();
tc.TextureID = textures["pictures/opentksquare.png"];
objects.Add(tc);
TexturedCube tc2 = new TexturedCube();
tc2.Position += new Vector3(1f, 1f, 1f);
tc2.TextureID = textures["pictures/opentksquare2.png"];
objects.Add(tc2);
cam.Position += new Vector3(0f, 0f, 3f);
}
protected override void OnRenderFrame(FrameEventArgs e)
{
base.OnRenderFrame(e);
GL.Viewport(0, 0, Width, Height);
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
GL.Enable(EnableCap.DepthTest);
shaders[activeShader].EnableVertexAttribArrays();
int indiceat = 0;
foreach (Volume v in objects)
{
GL.ActiveTexture(TextureUnit.Texture0);
GL.BindTexture(TextureTarget.Texture2D, v.TextureID);
GL.UniformMatrix4(shaders[activeShader].GetUniform("modelview"), false, ref v.ModelViewProjectionMatrix);
if (shaders[activeShader].GetUniform("maintexture") != -1)
{
GL.Uniform1(shaders[activeShader].GetUniform("maintexture"), 0);
}
GL.DrawElements(BeginMode.Triangles, v.IndiceCount, DrawElementsType.UnsignedInt, indiceat * sizeof(uint));
indiceat += v.IndiceCount;
}
shaders[activeShader].DisableVertexAttribArrays();
GL.Flush();
SwapBuffers();
}
private void ProcessInput()
{
if(OpenTK.Input.Keyboard.GetState().IsKeyDown(Key.Escape))
{
Exit();
}
if (OpenTK.Input.Keyboard.GetState().IsKeyDown(Key.W))
{
cam.Move(0f, 0.1f, 0f);
}
if (OpenTK.Input.Keyboard.GetState().IsKeyDown(Key.S))
{
cam.Move(0f, -0.1f, 0f);
}
if (OpenTK.Input.Keyboard.GetState().IsKeyDown(Key.A))
{
cam.Move(-0.1f, 0f, 0f);
}
if (OpenTK.Input.Keyboard.GetState().IsKeyDown(Key.D))
{
cam.Move(0.1f, 0f, 0f);
}
if (OpenTK.Input.Keyboard.GetState().IsKeyDown(Key.Q))
{
cam.Move(0f, 0f, 0.1f);
}
if (OpenTK.Input.Keyboard.GetState().IsKeyDown(Key.E))
{
cam.Move(0f, 0f, -0.1f);
}
if (Focused)
{
Vector2 delta = lastMousePos - new Vector2(OpenTK.Input.Mouse.GetState().X, OpenTK.Input.Mouse.GetState().Y);
lastMousePos += delta;
cam.AddRotation(delta.X, delta.Y);
lastMousePos = new Vector2(OpenTK.Input.Mouse.GetState().X, OpenTK.Input.Mouse.GetState().Y);
}
}
protected override void OnUpdateFrame (FrameEventArgs e){
time += (float)e.Time;
ProcessInput();
// base.OnUpdateFrame();
Vector2[] texcoorddata;
List<Vector3> verts = new List<Vector3>();
List<int> inds = new List<int>();
List<Vector3> colors = new List<Vector3>();
List<Vector2> texcoords = new List<Vector2>();
int vertcount = 0;
foreach (Volume v in objects)
{
verts.AddRange(v.GetVerts().ToList());
inds.AddRange(v.GetIndices(vertcount).ToList());
colors.AddRange(v.GetColorData().ToList());
texcoords.AddRange(v.GetTextureCoords());
texcoorddata = texcoords.ToArray();
// texcoords.AddRange(v.GetTextureCoords());
vertcount += v.VertCount;
}
vertdata = verts.ToArray();
indicedata = inds.ToArray();
coldata = colors.ToArray();
texcoorddata = texcoords.ToArray();
GL.BindBuffer(BufferTarget.ArrayBuffer, shaders[activeShader].GetBuffer("vPosition"));
GL.BufferData<Vector3>(BufferTarget.ArrayBuffer, (IntPtr)(vertdata.Length * Vector3.SizeInBytes), vertdata, BufferUsageHint.StaticDraw);
GL.VertexAttribPointer(shaders[activeShader].GetAttribute("vPosition"), 3, VertexAttribPointerType.Float, false, 0, 0);
if (shaders[activeShader].GetAttribute("vColor") != -1)
{
GL.BindBuffer(BufferTarget.ArrayBuffer, shaders[activeShader].GetBuffer("vColor"));
GL.BufferData<Vector3>(BufferTarget.ArrayBuffer, (IntPtr)(coldata.Length * Vector3.SizeInBytes), coldata, BufferUsageHint.StaticDraw);
GL.VertexAttribPointer(shaders[activeShader].GetAttribute("vColor"), 3, VertexAttribPointerType.Float, true, 0, 0);
}
if (shaders[activeShader].GetAttribute("texcoord") != -1)
{
GL.BindBuffer(BufferTarget.ArrayBuffer, shaders[activeShader].GetBuffer("texcoord"));
GL.BufferData<Vector2>(BufferTarget.ArrayBuffer, (IntPtr)(texcoorddata.Length * Vector2.SizeInBytes), texcoorddata, BufferUsageHint.StaticDraw);
GL.VertexAttribPointer(shaders[activeShader].GetAttribute("texcoord"), 2, VertexAttribPointerType.Float, true, 0, 0);
}
// objects[0].Position = new Vector3(0.3f, -0.5f + (float) Math.Sin(time), -3.0f);
objects[0].Rotation = new Vector3(0.55f * time, 0.25f * time, 0);
objects[0].Scale = new Vector3(1, 1, 1);
objects[1].Position = new Vector3(-5f, 0.5f + (float)Math.Cos(time), -2.0f);
objects[1].Rotation = new Vector3(-0.25f * time, -0.35f * time, 0);
objects[1].Scale = new Vector3(2, 1, 2);
foreach (Volume v in objects)
{
v.CalculateModelMatrix();
v.ViewProjectionMatrix = cam.GetViewMatrix() * Matrix4.CreatePerspectiveFieldOfView(1.3f, ClientSize.Width / (float)ClientSize.Height, 1.0f, 40.0f);
v.ModelViewProjectionMatrix = v.ModelMatrix * v.ViewProjectionMatrix;
}
//GL.UniformMatrix4(uniform_mview, false, ref mviewdata[0]);
GL.UseProgram(shaders[activeShader].ProgramID);
GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
GL.BindBuffer(BufferTarget.ElementArrayBuffer, ibo_elements);
GL.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(indicedata.Length * sizeof(int)), indicedata, BufferUsageHint.StaticDraw);
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
GL.Viewport(ClientRectangle.X, ClientRectangle.Y, ClientRectangle.Width, ClientRectangle.Height);
Matrix4 projection = Matrix4.CreatePerspectiveFieldOfView((float)Math.PI / 4, Width / (float)Height, 1.0f, 64.0f);
GL.MatrixMode(MatrixMode.Projection);
GL.LoadMatrix(ref projection);
}
void loadShader(String filename,ShaderType type, int program, out int address)
{
address = GL.CreateShader(type);
try{
using (StreamReader sr = new StreamReader(filename))
{
GL.ShaderSource(address, sr.ReadToEnd());
}
}
catch (Exception ex){
Console.WriteLine("error loading shader : " + ex);
}
GL.CompileShader(address);
GL.AttachShader(program, address);
Console.WriteLine(GL.GetShaderInfoLog(address));
}
///
///This function will load an image as a bitmap,
/// and then store the data of it in the graphics
/// card's memory. It returns an int, the address
/// of the texture so that we can retrieve the data
/// when we want to draw the texture.
int loadImage(Bitmap image){
int texID = GL.GenTexture();
GL.BindTexture(TextureTarget.Texture2D, texID);
BitmapData data = image.LockBits(new System.Drawing.Rectangle(0, 0, image.Width, image.Height),
ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, data.Width, data.Height, 0,
OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, data.Scan0);
image.UnlockBits(data);
GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
return texID;
}
int loadImage(string filename)
{
try
{
Bitmap file = new Bitmap(filename);
return loadImage(file);
}
catch (Exception e)
{
Console.WriteLine(e);
return -1;
}
}
}
}
You can see all my referances for the project in the picture
The mistake is very simple. The attribute TextureID exists twice. Fist it is declared in the class Volume:
public abstract class Volume
{
// [...]
public int TextureID;
// [...]
}
Then an attribute with the same name is declared in the class TexturedCube, which is (indirectly) derived from Volume:
public class TexturedCube : Cube
{
// [...]
public int TextureID;
// [...]
}
Hence the 2nd attribute covers the 1st one. Remove the attribute from the class TexturedCube:
By the way, the same applies to the IsTextured and TextureCoordsCount attribute.
I am trying to create a high level API with sharpdx. It has to be able to draw, but I am stuck on how to make it work with multiple Draw calls at the same time.
This is how I call the class
DirectXFinalD d33d = new DirectXFinalD();
Here I create a new object of the type viereck(rectangle)
Viereck viereck = new Viereck(0, 0, 0.2, 0.1, myBrush, myBrush, 1, false);
Here I pass the object to the class
d33d.DrawDirectX(viereck);
And it already works, but the problem is, I want it to be able that you can pass more objects at any given time, and let them be drawn.
I already tried to always update the vertexbuffer and always += the vertices, but the problem is that different shapes need different topologies. Here is the class:
namespace DrawHost
{
public class DirectXFinalD : DrawHost.DirectXBaseD<D3D11>, IDrawable
{
;
public DirectXFinalD(IDrawable objectToDraw = null, DataStream stream = null)
{
this.objectToDraw = objectToDraw;
if (stream == null)
stream = new DataStream(32 * 612500, true, true);
else
this.stream = stream;
}
protected override void Attach()
{
#region Shader
if (Renderer == null)
return;
device = Renderer.Device;
context = device.ImmediateContext;
// Compile Vertex and Pixel shaders
vertexShaderByteCode = ShaderBytecode.CompileFromFile("MiniTri.fx", "VS", "vs_4_0", ShaderFlags.None, EffectFlags.None);
vertexShader = new VertexShader(device, vertexShaderByteCode);
pixelShaderByteCode = ShaderBytecode.CompileFromFile("MiniTri.fx", "PS", "ps_4_0", ShaderFlags.None, EffectFlags.None);
pixelShader = new PixelShader(device, pixelShaderByteCode);
// Layout from VertexShader input signature
layout = new InputLayout(device, ShaderSignature.GetInputSignature(vertexShaderByteCode), new[] {
new InputElement("POSITION",0,Format.R32G32B32A32_Float,0,0),
new InputElement("COLOR",0,Format.R32G32B32A32_Float,16,0)
});
#endregion
if (objectToDraw == null) { }
else
{
float r = 0;
float g = 0;
float b = 0;
switch (objectToDraw.ToString())
{
#region Dreieck
case "Dreieck":
Dreieck dreieck = (Dreieck)objectToDraw;
topology = PrimitiveTopology.TriangleStrip;
ConvertColor(ref r, ref g, ref b, ((System.Windows.Media.SolidColorBrush)dreieck.Color).Color);
streamList.Add(new Vector4((Convert.ToSingle(dreieck.X)), (Convert.ToSingle(dreieck.Y) / 10), 0f, 1.0f)); streamList.Add(new Vector4(r, g, b, 1.0f));
streamList.Add(new Vector4(((Convert.ToSingle(dreieck.X) + Convert.ToSingle(dreieck.Width))), -(Convert.ToSingle(dreieck.Y)), 0f, 1.0f)); streamList.Add(new Vector4(r, g, b, 1.0f));
streamList.Add(new Vector4(-(Convert.ToSingle(dreieck.X)), -((Convert.ToSingle(dreieck.Y) + Convert.ToSingle(dreieck.Height) )), 0f, 1.0f)); streamList.Add(new Vector4(r, g, b, 1.0f));
break;
#endregion
#region Viereck
case "Viereck":
Viereck viereck = (Viereck)objectToDraw;
topology = PrimitiveTopology.TriangleStrip;
ConvertColor(ref r, ref g, ref b, ((System.Windows.Media.SolidColorBrush)viereck.Color).Color);
streamList.Add(new Vector4((Convert.ToSingle(viereck.X)), (Convert.ToSingle(viereck.Y)), 0f, 1.0f)); streamList.Add(new Vector4(r, g, b, 1.0f));// ok
streamList.Add(new Vector4(((Convert.ToSingle(viereck.X))), (Convert.ToSingle(viereck.Y) + Convert.ToSingle(viereck.Height)), 0f, 1.0f)); streamList.Add(new Vector4(r, g, b, 1.0f));// ok
streamList.Add(new Vector4((Convert.ToSingle(viereck.X) + Convert.ToSingle(viereck.Width)), (Convert.ToSingle(viereck.Y) ), 0f, 1.0f)); streamList.Add(new Vector4(r, g, b, 1.0f));// ok
streamList.Add(new Vector4((Convert.ToSingle(viereck.X) + Convert.ToSingle(viereck.Width)), ((Convert.ToSingle(viereck.Y) + Convert.ToSingle(viereck.Height))), 0f, 1.0f)); streamList.Add(new Vector4(r, g, b, 1.0f));// ok
break;
#endregion
#region Kreis
case "Kreis":
topology = PrimitiveTopology.Undefined;
Kreis kreis = (Kreis)objectToDraw;
ConvertColor(ref r, ref g, ref b, ((System.Windows.Media.SolidColorBrush)kreis.Color).Color);
for (float j = 0; j <= 360; j++)
{
for (double i = 0; i <= 360; i++) //254
{
double rad = i * (Math.PI / 180);
float x = (float)Math.Cos(rad) * ((float)kreis.Width / 2);
float y = (float)Math.Sin(rad) * ((float)kreis.Height / 2);
streamList.Add(new Vector4(x , y, 0f, 1.0f)); streamList.Add(new Vector4(r, g, b, 1.0f));
}
}
break;
#endregion
};
foreach (Vector4 a in streamList)
{
stream.WriteRange(new[] { a });
}
stream.Position = 0;
streamGV streamGV = new streamGV(stream);
//streamGV.GetList(streamList);
//streamList = null;
GC.Collect();
}
vertices = new Buffer(device, stream, new BufferDescription()
{
BindFlags = BindFlags.VertexBuffer,
CpuAccessFlags = CpuAccessFlags.None,
OptionFlags = ResourceOptionFlags.None,
SizeInBytes = (int)stream.Length,
Usage = ResourceUsage.Default,
StructureByteStride = 0,
});
stream.Dispose();
// Prepare All the stages
context.InputAssembler.InputLayout = (layout);
context.InputAssembler.PrimitiveTopology = topology;
context.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(vertices, 32, 0));
context.VertexShader.Set(vertexShader);
context.PixelShader.Set(pixelShader);
}
public override void RenderScene(DrawEventArgs args)
{
Renderer.Device.ImmediateContext.ClearRenderTargetView(Renderer.RenderTargetView, new Color4(0.6f, 0, 0, 0));
Renderer.Device.ImmediateContext.Draw((int)stream.Length, 0);
return;
}
public override void Case(DXElement dxviewer)
{
dxviewer11 = dxviewer;
}
public override void DrawDirectX(IDrawable objectToDraw)
{
this.objectToDraw = objectToDraw;
//dxviewer11.Renderer = new Scene_11();
//Renderer = new D3D11();
stream = new DataStream(32 * 612500, true, true);
streamGV strean = new streamGV();
dxviewer11.Renderer = new DirectXFinalD(objectToDraw, stream) { Renderer = new D3D11() };
}
private void ConvertColor(ref float r, ref float g, ref float b, System.Windows.Media.Color color)
{
r = (float)(color.R * 255);
g = (float)(color.G * 255);
b = (float)(color.B * 255);
}
}
How can I make it possible to draw all of them at the same time? I am using sharpdx as my renderform. One problem is that I always have to change the topology, for example triangle needs trianglelistbut for the circle I use Linestrip. Any help would be appreciated
As I can see you're calling the Attach ones. In this attach method you're only creating 1 vertex buffer, with a mesh depending on objectToDraw.
You should decouple you shader compilation code and your vertexbuffer setup.
You could create a class that will manage the vertexbuffer and 'knows' how to draw the mesh.
For example: (PSEUDO)
[StructLayout(LayoutKind.Sequential)]
public struct Vertex
{
public const int Stride = 16 + 16;
public Vector4 Pos;
public Color4 Color;
}
public class Mesh
{
private Vertex[] _vertices;
private int[] _indices;
private SharpDX.Direct3D11.Buffer _indexBuffer;
private SharpDX.Direct3D11.Buffer _vertexBuffer;
private VertexBufferBinding _vertexBufferBinding;
public Mesh(Vertex[] vertices, int[] indices)
{
// save the vertices in a field
_vertices = value;
var vbd = new BufferDescription(
SharpDX.Utilities.SizeOf<Vertex>() * _vertices.Length,
ResourceUsage.Immutable,
BindFlags.VertexBuffer,
CpuAccessFlags.None,
ResourceOptionFlags.None,
0);
// setup the vertex buffer
_vertexBuffer = SharpDX.Direct3D11.Buffer.Create<Vertex>(DX11.Device, _vertices, vbd);
// create the binding
_vertexBufferBinding = new VertexBufferBinding(_vertexBuffer, Vertex.Stride, 0);
_indices = value;
var ibd = new BufferDescription(
sizeof(int) * _indices.Length,
ResourceUsage.Immutable,
BindFlags.IndexBuffer,
CpuAccessFlags.None,
ResourceOptionFlags.None,
0);
// setup the index buffer
_indexBuffer = SharpDX.Direct3D11.Buffer.Create<int>(DX11.Device, _indices, ibd);
}
// the SelectBuffers will select the right vertex buffer.
// this could be combined with the Draw method, but I rather not
// You should call this ones even when you draw multiple the same mesh.
public void SelectBuffers()
{
DX11.Device.ImmediateContext.InputAssembler.SetVertexBuffers(0, _vertexBufferBinding);
DX11.Device.ImmediateContext.InputAssembler.SetIndexBuffer(_indexBuffer, SharpDX.DXGI.Format.R32_UInt, 0);
}
public void Draw()
{
DX11.Device.ImmediateContext.DrawIndexed(_indices.Length, 0, 0);
}
}
List<Mesh> _meshes = new List<Mesh>();
public void SetupShaders()
{
vertexShaderByteCode = ShaderBytecode.CompileFromFile("MiniTri.fx", "VS", "vs_4_0", ShaderFlags.None, EffectFlags.None);
vertexShader = new VertexShader(device, vertexShaderByteCode);
pixelShaderByteCode = ShaderBytecode.CompileFromFile("MiniTri.fx", "PS", "ps_4_0", ShaderFlags.None, EffectFlags.None);
...... etc
}
public Mesh SetupMesh(object objectToDraw)
{
switch(.....)
{
// .. implement your beautiful switch ;-)
}
return new Mesh(vertices, indices);
}
public void Init()
{
SetupShaders();
_meshes.Add(SetupMesh(new Dreieck(.....)));
_meshes.Add(SetupMesh(new Viereck(.....)));
_meshes.Add(SetupMesh(new Kreis(.....)));
}
public override void RenderScene(DrawEventArgs args)
{
Renderer.Device.ImmediateContext.ClearRenderTargetView(Renderer.RenderTargetView, new Color4(0.6f, 0, 0, 0));
foreach(var mesh in _meshes)
{
mesh.SelectBuffers();
mesh.Draw();
}
return;
}
Something like that...
Trying to display a graph on a Form application.
Created a PictureBox control and initialized this class with its value.
On resize, graph always updates; on mouse scroll, it hardly does.
It's GraphBox , PictureBox control, inside a GraphBoxPanel, Panel control.
This the class:
public struct DLT_measure_item
{
public DateTime ts;
public float value;
public int id;
public int X;
public int Y;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct dlt_ser_meas
{
public byte msg_id; // 'D'
public byte meas_count; // Number of measures
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public byte[] port; // Module ID (4b) + Port ID (4b)
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public float[] meas; // measure
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public byte[] msg_end;
}
public class manageGraph
{
private PictureBox box;
public bool displayGrid = true;
private int horAxisMin_p = 0;
private int horAxisMax_p = 300;
private float verAxisMin_p = 0;
private float verAxisMax_p = 40;
public int horAxisMin
{
get { return this.horAxisMin_p; }
set
{
if (value < horAxisMax_p)
{
this.horAxisMin_p = value;
reDraw();
}
}
}
public int horAxisMax
{
get { return this.horAxisMax_p; }
set
{
if (value > horAxisMin_p)
{
this.horAxisMax_p = value;
reDraw();
}
}
}
public float verAxisMin
{
get { return this.verAxisMin_p; }
set
{
if (value < verAxisMax_p)
{
this.verAxisMin_p = value;
verPointPerUnit = graphArea.Height / (verAxisMax_p - this.verAxisMin_p);
}
}
}
public float verAxisMax
{
get { return this.verAxisMax_p; }
set
{
if (value > verAxisMin_p)
{
this.verAxisMax_p = value;
verPointPerUnit = graphArea.Height / (this.verAxisMax_p - verAxisMin_p);
}
}
}
Pen axes = new Pen(Color.Black, (float)1.5);
public int horAxisSpacing = 30;
public int verAxisSpacing = 20;
public int horAxis = 20;
public int verAxis = 20;
private float horPointPerUnit = 1;
private float verPointPerUnit = 1;
public int horAxisTickLen = 5;
public int verAxisTickLen = 5;
public bool horAxisShowTime = false;
private Rectangle graphArea = new Rectangle();
public void reDraw()
{
box.Image.Dispose();
Bitmap GraphBlankImage = new Bitmap(box.Width, box.Height);
box.Image = GraphBlankImage;
updatePointPerUnit();
drawGrid();
box.Refresh();
}
public manageGraph(PictureBox targetImageBoxbox)
{
box = targetImageBoxbox;
horAxisMin_p = 0;
horAxisMax_p = 300;
verAxisMin_p = 0F;
verAxisMax_p = 50F;
updatePointPerUnit();
}
private Point measToPoint(DLT_measure_item measure) {
Point coords = new Point();
coords.X = graphArea.Width - (int)(
((DateTime.Now - measure.ts).TotalSeconds + horAxisMin_p) * horPointPerUnit ) ;
coords.Y = graphArea.Height - (int)(
((measure.value - verAxisMin_p) * verPointPerUnit));
return coords;
}
public manageGraph(PictureBox targetImageBoxbox,
int xmin, int xmax, float ymin, float ymax)
{
box = targetImageBoxbox;
horAxisMin_p = xmin;
horAxisMax_p = xmax;
verAxisMin_p = ymin;
verAxisMax_p = ymax;
updatePointPerUnit();
}
private void updateGraphArea()
{
graphArea = new Rectangle(0, 0, box.Width - horAxis, box.Height - verAxis);
}
private void updatePointPerUnit()
{
updateGraphArea();
horPointPerUnit = graphArea.Width / (horAxisMax_p - horAxisMin_p);
verPointPerUnit = graphArea.Height / (verAxisMax_p - verAxisMin_p);
}
public void drawGrid()
{
//updatePointPerUnit();
using (Graphics g = Graphics.FromImage(box.Image))
{
// X axis
g.DrawLine(axes, graphArea.Left, graphArea.Bottom, box.Width, graphArea.Bottom);
// Y axis
g.DrawLine(axes, graphArea.Right + 1, graphArea.Top, graphArea.Right +1, graphArea.Bottom);
using (Font ArialFont = new Font("Arial", 10))
{
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
// Put x labels
for (int i = 1; i <= (graphArea.Width / horPointPerUnit); i = i + (int)(horAxisSpacing / horPointPerUnit ) + 1)
{
g.DrawString((i).ToString(), ArialFont, Brushes.Black, graphArea.Width - ( i * horPointPerUnit) - 5, graphArea.Bottom +5);
g.DrawLine(axes, graphArea.Width - (i * horPointPerUnit), graphArea.Bottom + (horAxisTickLen / 2), graphArea.Width - (i * horPointPerUnit), graphArea.Bottom - (horAxisTickLen / 2));
}
// Put y labels
for (int i = 1; i <= (graphArea.Height / verPointPerUnit); i = i + (int)(verAxisSpacing / verPointPerUnit) +1)
{
g.DrawString((i).ToString(), ArialFont, Brushes.Black, graphArea.Right + 1 , graphArea.Height - (i * verPointPerUnit) - 8);
g.DrawLine(axes, graphArea.Width - (verAxisTickLen / 2), (i * verPointPerUnit), graphArea.Width + (verAxisTickLen / 2), (i * verPointPerUnit));
}
}
/*Put some random data*/
DLT_measure_item testmeas = new DLT_measure_item();
Point testGraphPoint = new Point();
testmeas.ts = DateTime.Now;
testmeas.value = 0;
testGraphPoint = measToPoint(testmeas);
g.FillEllipse(Brushes.Blue, testGraphPoint.X, testGraphPoint.Y, 4, 4);
for (double i = 0; i < 300; i++)
{
double x = i;
double freq = 10;
double y = 30 - (i/10);
testmeas.value = (float)y;
testmeas.ts = DateTime.Now.AddSeconds(-1 * i);
testGraphPoint = measToPoint(testmeas);
g.FillEllipse(Brushes.Red, testGraphPoint.X, testGraphPoint.Y, 2,2);
}
}
}
}
The initialization:
public DLThermalogMainForm()
{
InitializeComponent();
Bitmap GraphBlankImage = new Bitmap(GraphBox.Width, GraphBox.Height);
GraphBox.Image = GraphBlankImage;
myGraph = new manageGraph(GraphBox);
myGraph.drawGrid();
}
Those the handlers:
private void Form1_ResizeEnd(object sender, EventArgs e)
{
myGraph.reDraw();
OutputTextBox.AppendText("Resize." + Environment.NewLine);
}
private void GraphBox_MouseMove(object sender, MouseEventArgs e)
{
//Get the focus to have the wheel working
GraphBoxPanel.Focus();
}
private void GraphBoxPanel_MouseWheel(object sender, MouseEventArgs e)
{
// Change x axis max value to zoom in/out the graph
myGraph.horAxisMax += e.Delta/ 120;
myGraph.reDraw();
}
On resize event, it always redraws; on mouse wheel, it does quickly only with small horAxisMax values (does it makes sense???), but for larger, it takes many seconds to update, or doesn't at all.
Thank you very much
Change reDraw like this:
public void reDraw()
{
box.Image.Dispose();
Bitmap GraphBlankImage = new Bitmap(box.ClientSize.Width, box.ClientSize.Height);
updatePointPerUnit();
drawGrid(GraphBlankImage);
box.Image = GraphBlankImage;
}
and drawGrid like this:
public void drawGrid(Bitmap bmp)
{
//updatePointPerUnit(); //??
using (Graphics g = Graphics.FromImage(bmp))
{
...
...
...
}
}
Now the Bitmap with the grid should immediately show up in the PictureBox.
As mentioned a Graphics object is a tool to change an associated Bitmap. To pick it up the Bitmap should be assigned to the PictureBoxe's Image.
Also note, that unless your PictureBox has no Border, there is a small difference between the outer size (aka Bounds) and the inner size, the ClientRectangle / ClientSize. The Image should have the ClientSize
You may wonder why your original code doesn't work? After all an Image is a reference type, so changing it, as you did should be enough..
But looking deeper into the source code we find the reason:
The PictureBox's Image is a property and in its setter there is a call to InstallNewImage:
public Image Image {
get {
return image;
}
set {
InstallNewImage(value, ImageInstallationType.DirectlySpecified);
}
}
The same call is also in a few other places like Load or in the setter of ImageLocation. But changing the Image behind the scene alone will not force the PictureBox to make that call. A Refresh() should also do it.. And, as you found out, resizing it will also cause the PictureBox to pick up the changed data in the Image Bitmap..
The easiest way to force updating is simply to invalidate the control.
timer = new Timer();
timer.Interval = 200; //refreshes every 200 ms
timer.Tick += (sender,e) => targetImageBoxbox.Invalidate();
timer.Start();