I have a class called PointCloud that is a vbo buffer object and I'm rendering a couple of these with different data, one as a sphere and one as a dataset of xyz, the dataset has colors and sphere is without colors. the problem is both seem to share the same colors, instead of having individual sets of colors, the colors for the sphere are actually set as null so I'm confused as to why color is being applied to it.
What I'm trying to do is draw the sphere 'white' while the other pointcloud is it's respective color.
if you can't see the image try this (http://s8.postimg.org/q1j0nlkol/pcerror.png)
http://pastebin.com/3fM9K87A
My question is can anybody explain why this is happening and what I'm doing wrong?
here is the pointcloud class:
using System;
using OpenTK.Graphics.OpenGL;
using OpenTK;
internal class PointCloud : IDisposable
{
protected int[] vbo_id = new int[2];
protected int vbo_size;
public bool HasColor;
public float[] Vertices = null;
public int[] Colors = null;
public float PointSize { get; set; }
public bool Visible { get; set; }
~PointCloud()
{
EmptyBuffer();
}
private void EmptyBuffer()
{
Vertices = (float[])null;
Colors = (int[])null;
}
public void Delete()
{
Dispose();
}
public PointCloud(float[] points)
{
this.vbo_size = points.Length;
GL.GenBuffers(2, this.vbo_id);
GL.BindBuffer(BufferTarget.ArrayBuffer, this.vbo_id[0]);
GL.BufferData<float>(BufferTarget.ArrayBuffer, new IntPtr(points.Length * BlittableValueType.StrideOf<float>(points)), points, BufferUsageHint.StaticDraw);
Vertices = points;
}
public void ApplyColorMap(int[] colors)
{
GL.BindBuffer(BufferTarget.ArrayBuffer, this.vbo_id[1]);
GL.BufferData<int>(BufferTarget.ArrayBuffer, new IntPtr(colors.Length * BlittableValueType.StrideOf<int>(colors)), colors, BufferUsageHint.StaticDraw);
Colors = colors;
this.HasColor = true;
}
public void Render(FrameEventArgs e)
{
GL.EnableClientState(ArrayCap.VertexArray);
GL.BindBuffer(BufferTarget.ArrayBuffer, this.vbo_id[0]);
GL.VertexPointer(3, VertexPointerType.Float, Vector3.SizeInBytes, new IntPtr(0));
GL.DrawArrays(BeginMode.Points, 0, this.vbo_size);
if (!this.HasColor)
{
return;
}
GL.BindBuffer(BufferTarget.ArrayBuffer, this.vbo_id[1]);
GL.ColorPointer(4, ColorPointerType.UnsignedByte, 4, IntPtr.Zero);
GL.EnableClientState(ArrayCap.ColorArray);
}
public void Dispose()
{
EmptyBuffer();
GL.DeleteBuffers(vbo_id.Length, vbo_id);
this.vbo_id = new int[2];
}
}
this is my render code:
protected override void OnRenderFrame(FrameEventArgs e)
{
base.OnRenderFrame(e);
GL.MatrixMode(MatrixMode.Modelview);
GL.Clear(ClearBufferMask.DepthBufferBit | ClearBufferMask.ColorBufferBit);
GL.LoadMatrix(ref this.CameraMatrix);
GL.PushMatrix();
this.pointclouds.ForEach((Action<PointCloud>)(i =>
{
//blend
GL.Enable(EnableCap.Blend);
if (i.Visible)
{
GL.PushMatrix();
GL.PointSize(2.0f);
if (!i.HasColor)
{
GL.Color4(Color.White);
}
i.Render(e);
GL.PopMatrix();
}
}));
GL.PopMatrix();
}
The reason that it is not working is that you never disable clientState(ArrayCap.ColorArray). This will cause OpenGL to reuse this information endlessly.
A good approach is before you leave your render method to ALWAYS disable all client states that you enabled at the beginning of your function.
In your case you can simplify things by removing your color VBO completely, since your points only have a single color anyway. You already try to do this for !hasColor case anyway
Secondly, and I'm not sure if it was on purpose. In your render function you never set the correct color: When you call drawArrays you sent the information to the GPU, and after that you load the colors. This will cause the next point to have the previous color.
Something like this:
public void Render(FrameEventArgs e)
{
GL.EnableClientState(ArrayCap.VertexArray);
GL.BindBuffer(BufferTarget.ArrayBuffer, this.vbo_id[0]);
GL.VertexPointer(3, VertexPointerType.Float, Vector3.SizeInBytes, new IntPtr(0));
if (!this.HasColor) GL.Color4f(0,0,0);
else GL.Color4f(putcolorvalue);
GL.DrawArrays(BeginMode.Points, 0, this.vbo_size);
GL.DisableClientState(ArrayCap.VertexArray);
}
I think you have to disable the ArrayCap.ColorArray state, if not the latest applied color array will be used:
GL.DisableClientState(ArrayCap.ColorArray);
Related
Is there any replacement (analogue) for CSS3 function repeating-linear-gradient() in .NET (WinForms, not WPF)?
I need to paint repeating "zebra stripes" (e.g. red, blue, green, red, blue, green, ...) at an angle 45 degrees.
UPD:
Following Jimi's advice I managed to solve the problem only partially:
private void DrawRepeatingStripes(int degree, int stripeWidth, Color[] colors, Rectangle rect, Graphics graphics)
{
using (var img = new Bitmap(colors.Length * stripeWidth, rect.Height))
{
using (var g = Graphics.FromImage(img))
{
for (int i = 0; i < colors.Length; i++)
{
// TODO: cache SolidBrush
g.FillRectangle(new SolidBrush(colors[i]), stripeWidth * i, 0, stripeWidth, rect.Height);
}
}
using (var tb = new TextureBrush(img, WrapMode.Tile))
{
using (var myMatrix = new Matrix())
{
myMatrix.Rotate(degree);
graphics.Transform = myMatrix;
graphics.FillRectangle(tb, rect);
graphics.ResetTransform();
}
}
}
}
Usage (in some form's code):
protected override void OnPaintBackground(PaintEventArgs e)
{
base.OnPaintBackground(e);
DrawRepeatingStripes(45, 10, new Color[] { Color.Red, Color.Yellow, Color.Green }, e.ClipRectangle, e.Graphics);
}
The problem is that rotation is... well, a rotation, so part of rect is filled with stripes and part is empty. Have no idea how to solve it :(
An example about using a TextureBrush to fill the surface of a Control used as canvas.
The LinearRepeatingGradient class exposes a bindable ColorBands Property (of Type BindingList<ColorBand>) that allows to add or remove ColorBand objects, a record that defines the Color and size of each band you want to generate.
The RotationAngle Property specifies the rotation to apply to the rendering.
In the Paint event of the Control used as canvas, call the Fill(Graphics g) method, passing the e.Graphics object provided by the PaintEventArgs argument.
A new Bitmap is generated, based on the content of the ColorBands Property.
When the rotation angle cannot be exactly divided by 90, the canvas' dimensions are inflated by a third of its diagonal (as the maximum distance from the non-rotated rectangle).
The TextureBrush fills this inflated surface, so no blank space is left on the sides of the canvas.
Since this test sample is built with .NET 7, I'm using record to store the color bands' settings. You can replace it with a class object without changes to the rest of the code.
public record ColorBand(Color Color, int Size) {
public override string ToString() => $"Color: {Color.Name} Size: {Size}";
}
Same as above: using declaration instead of using statements
using System.Drawing;
using System.Drawing.Drawing2D;
public class LinearRepeatingGradient
{
public LinearRepeatingGradient(float rotation = .0f)
{
ColorBands = new BindingList<ColorBand>();
RotationAngle = rotation;
}
public float RotationAngle { get; set; }
[Bindable(true), ListBindable(BindableSupport.Default)]
public BindingList<ColorBand> ColorBands { get; }
public void Fill(Graphics g) => Fill(g, g.ClipBounds);
public void Fill(Graphics g, Rectangle fillArea) => Fill(g, new RectangleF(fillArea.Location, fillArea.Size));
protected virtual void Fill(Graphics g, RectangleF display)
{
if (ColorBands is null || ColorBands.Count == 0 || g.Clip.IsInfinite(g)) return;
var canvas = InflateCanvas(display);
var centerPoint = new PointF(canvas.X + canvas.Width / 2, canvas.Y + canvas.Height / 2);
using var texture = GetTexture(canvas.Width);
if (texture is null) return;
using var brush = new TextureBrush(texture, WrapMode.Tile);
using var mx = new Matrix();
mx.RotateAt(RotationAngle, centerPoint);
g.Transform = mx;
g.FillRectangle(brush, canvas);
g.ResetTransform();
}
private RectangleF InflateCanvas(RectangleF rect)
{
if (RotationAngle % 90.0f == 0) return rect;
float maxInflate = (float)Math.Sqrt(Math.Pow(rect.X - rect.Right, 2) +
Math.Pow(rect.Y - rect.Bottom, 2)) / 3.0f;
var canvas = rect;
canvas.Inflate(maxInflate, maxInflate);
return canvas;
}
private Bitmap? GetTexture(float width)
{
int height = ColorBands!.Sum(c => c.Size);
if (height <= 0) return null;
var texture = new Bitmap((int)(width + .5f), height);
int startPosition = 0;
using var g = Graphics.FromImage(texture);
for (int i = 0; i < ColorBands!.Count; i++) {
var rect = new Rectangle(0, startPosition, texture.Width, ColorBands![i].Size);
using var brush = new SolidBrush(ColorBands![i].Color);
g.FillRectangle(brush, rect);
startPosition += ColorBands![i].Size;
}
return texture;
}
}
This is how it works:
Since the ColorBands property is bindable, you can use data bindings to perform actions, when a ColorBand object is added or removed and also bind the ColorBands collection to Controls, as shown in the animation:
public partial class SomeForm : Form {
LinearRepeatingGradient gradient = new();
public SomeForm()
{
InitializeComponent();
[DataGridView].DataSource = gradient.ColorBands;
gradient.ColorBands.ListChanged += (s, e) => someControl.Invalidate();
}
private void someControl_Paint(object sender, PaintEventArgs e) => gradient.Fill(e.Graphics);
As a consequence, when you add a new ColorBand (or remove it), the internal collection changes and the Control used as canvas is invalidated, showing the new fill:
gradient.ColorBands.Add(new ColorBand(Color.Red, 45f));
The RotationAngle property doesn't use data bindings, so you have to invalidate the canvas manually when you change it. You can of course change that and make this property bindable:
gradient.RotationAngle = 215f;
someControl.Invalidate();
I am trying to draw a triangle shape in Android Xamarin with C#. The triangle is a class with the method draw. In this draw method, there is an openGL20 method for coloring the triangle object we just created. Whenever the execution in the draw method reaches this color object method, this exception is thrown Java.Lang.IllegalArgumentException Message=length - offset < count*4 < needed.
I really can't understand what that error means but here is the code am using so far
public class MyGLRenderer : Java.Lang.Object, GLSurfaceView.IRenderer
{
//Renderer method to draw the triangle object
public void OnDrawFrame(IGL10 gl)
{
GLES20.GlClear(GLES20.GlColorBufferBit);
Triangle triangle = new Triangle();
triangle.draw();
}
//Method to set the view and the height of the painting window
public void OnSurfaceChanged(IGL10 gl, int width, int height)
{
GLES20.GlViewport(0, 0, width, height);
}
public void OnSurfaceCreated(IGL10 gl, Javax.Microedition.Khronos.Egl.EGLConfig config)
{
//Set the background frame color
GLES20.GlClearColor(0.0f, 0.0f, 1.0f, 0.0f);
GLES20.GlDrawArrays(GLES20.GlColorBufferBit, 2, 10);
}
}
Code below defines the Triangle class and the OPENGL20 color method thats throwing an exception commented upon
public class Triangle
{
private FloatBuffer vertexBuffer;
//Number of co-ordinates per vertex in this context
private static readonly int Coords_per_vert = 3;
private static float[] triangleCoords = new float[] {
0.0f,0.622008459f,0.0f, //top
-0.5f,-0.311004243f,0f , //bottom left
0.5f, -0.311004243f,0.0f //bottom right
};
//Set color with red, green, blue and alpha values
private float[] color = new float[] { 0.63671875f, 0.76953125f, 0.22265625f };
private readonly int mProgram;
public Triangle()
{
//Initialize vertex byte buffer for shape co-ordinates
ByteBuffer bb = ByteBuffer.AllocateDirect(triangleCoords.Length * 4);
//Use the device native byte order
bb.Order(ByteOrder.NativeOrder());
FloatBuffer myfloat = bb.AsFloatBuffer();
//Create floating point buffer from ByteBuffer
vertexBuffer = bb.AsFloatBuffer();
vertexBuffer.Put(triangleCoords);
vertexBuffer.Position(0);
int vertexShader = MyGLRenderer.loadShader(GLES20.GlVertexShader, vertexShaderCode);
int fragmentShader = MyGLRenderer.loadShader(GLES20.GlFragmentShader, fragmentShaderCode);
//Create empty opengles program
mProgram = GLES20.GlCreateProgram();
//Add vertex shader to program
GLES20.GlAttachShader(mProgram, vertexShader);
//Add Fragment to shader
GLES20.GlAttachShader(mProgram, fragmentShader);
//Create openGL program executables
GLES20.GlLinkProgram(mProgram);
}
private readonly string vertexShaderCode = "attribute vec4 vPosition;" +
"void main(){" +
" gl_Position=vPosition;" +
"}";
private readonly string fragmentShaderCode = "precision mediump float;" +
"uniform vec4 vColor;" +
"void main(){" +
"gl_FragColor=vColor;" +
"}";
private int positionHandle, colorHandle;
private readonly int vertexCount = triangleCoords.Length / Coords_per_vert;
private readonly int vertexStride = Coords_per_vert * 4;
//Create a method for drawing the triangle
public void draw()
{
//Add Program to open GLES Environment
GLES20.GlUseProgram(mProgram);
//Get handle to vertex shader's vPosition member
positionHandle = GLES20.GlGetAttribLocation(mProgram, "vPosition");
//Enable a handle to the triangle's vertices
GLES20.GlEnableVertexAttribArray(positionHandle);
//Prepare the triangle co ordinate data
GLES20.GlVertexAttribPointer(positionHandle, Coords_per_vert, GLES20.GlFloat, false, vertexStride, vertexBuffer);
//Get handle to fragment shader's vColor Member
colorHandle = GLES20.GlGetUniformLocation(mProgram, "vColor");
//Set color for drawing the triangle
GLES20.GlUniform4fv(colorHandle, 1, color, 3);
//Draw the triangle, this method causes an exception
GLES20.GlDrawArrays(GLES20.GlTriangles, 0, vertexCount);
//Disable vertex array
GLES20.GlDisableVertexAttribArray(positionHandle);
}
}
Please help me color the triangle object with success, what am i doing wrong
You are using a 3 sized float array for a vec4 uniform.
Add the alpha array to your color array like this
Replace
private float[] color = new float[] { 0.63671875f, 0.76953125f, 0.22265625f };
with
private float[] color = new float[] { 0.63671875f, 0.76953125f, 0.22265625f,1.0f};
with the last member of the second array representing the alpha or opacity of the color being drawn
I am writing a program in WPF, using SharpGL, to draw some cubes. In my software, a large number of cubes need to be drawn. Initially, I used the immediate mode rendering method, which makes the app too slow. After that I use the VAO method, the app speed improved a lot. The only problem is that all the cubes are drawn in white. I think the problem is the Shader part. Please help me fix this problem.
private void openGLControl_OpenGLInitialized(object sender, SharpGL.WPF.OpenGLRoutedEventArgs args)
{
OpenGL gL = openGLControl.OpenGL;
gL.Enable(OpenGL.GL_DEPTH_TEST);
gL.ClearColor(0, 0, 0, 0);
gL.MatrixMode(OpenGL.GL_PROJECTION);
gL.LoadIdentity();
myShader = new MyShader(gL);
}
public void SetUpCamera(OpenGL gL)
{
gL.Viewport(0, 0, (int)openGLControl.ActualWidth, (int)openGLControl.ActualHeight);
gL.MatrixMode(OpenGL.GL_PROJECTION);
gL.LoadIdentity();
gL.Perspective(45.0, openGLControl.ActualWidth / openGLControl.ActualHeight, 0.1, 10000.0);
gL.LookAt(0.0, 0.0, 0.1,
0.0, 0.0, 0.0,
0.0, 1.0, 0.0);
}
private void openGLControl_OpenGLDraw(object sender, SharpGL.WPF.OpenGLRoutedEventArgs args)
{
if (Camera.IsDrawSkip)
{
return;
}
Camera.IsDrawSkip = true;
OpenGL gL = openGLControl.OpenGL;
gL.ClearColor(Colors.DarkGray.ScR, Colors.DarkGray.ScG , Colors.DarkGray.ScB, 0);
gL.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT);
SetUpCamera(gL);
gL.MatrixMode(OpenGL.GL_MODELVIEW);
gL.LoadIdentity();
gL.Translate(Camera.XTransition, Camera.YTransition, Camera.Zoom);
gL.Rotate(Camera.XRotationAngle, 1, 0, 0);
gL.Rotate(Camera.YRotationAngle, 0, 1, 0);
myShader.Bind();
if (Camera.DrawState == DrawState.Draw)
{
drawObjects(gL);
}
VBOManager.Render(gL);
myShader.Unbind();
gL.EnableVertexAttribArray(1);
gL.Flush();
}
private void drawObjects(OpenGL gL)
{
if(int.TryParse(txtCount.Text, out int count))
{
VBOManager.Vertices.Clear();
int index = cbxShape.SelectedIndex;
if (index == 0)
{
for (int z = 0; z < count; z++)
{
for (int y = 0; y < count; y++)
{
for (int x = 0; x < count; x++)
{
VBOManager.AddCubeValues(x * 5, y * 5, z * 5, Colors.OrangeRed);
}
}
}
}
VBOManager.GenerateGeometry(gL);
}
}
public class MyShader
{
public MyShader(OpenGL gl)
{
gL = gl;
init();
}
#region Property
public OpenGL gL { get; set; }
public uint shader_id;
public uint shader_vp;
public uint shader_fp;
#endregion
public void init()
{
shader_vp = gL.CreateShader(OpenGL.GL_VERTEX_SHADER);
shader_fp = gL.CreateShader(OpenGL.GL_FRAGMENT_SHADER);
gL.ShaderSource(shader_vp, Shader_VERTEX_Text());
gL.ShaderSource(shader_fp, Shader_FRAGMENT_Text());
gL.CompileShader(shader_vp);
gL.CompileShader(shader_fp);
shader_id = gL.CreateProgram();
gL.AttachShader(shader_id, shader_fp);
gL.AttachShader(shader_id, shader_vp);
gL.LinkProgram(shader_id);
gL.BindAttribLocation(shader_id, 0, "in_Position"); // Bind a constant attribute location for positions of vertices
gL.BindAttribLocation(shader_id, 1, "in_Color"); // Bind another constant attribute location, this time for color
}
public void Bind()
{
gL.UseProgram(shader_id);
}
public void Unbind()
{
gL.UseProgram(0);
}
private string Shader_FRAGMENT_Text()
{
return #"#version 150 core
in vec3 pass_Color;
out vec4 out_Color;
void main(void)
{
out_Color = vec4(pass_Color, 1.0);
}";
}
private string Shader_VERTEX_Text()
{
return #"##version 150 core
in vec3 in_Position;
in vec3 in_Color;
out vec3 pass_Color;
void main(void)
{
gl_Position = vec4(in_Position, 1.0);
pass_Color = in_Color;
}";
}
}
public static class VBOManager
{
private static VertexBufferArray m_vertexBuffer = new VertexBufferArray();
public static List<float> Vertices = new List<float>();
public static List<float> Colores = new List<float>();
public static void GenerateGeometry(SharpGL.OpenGL gl)
{
m_vertexBuffer.Create(gl);
m_vertexBuffer.Bind(gl);
GenerateVertexBuffer(gl);
GenerateColourBuffer(gl);
m_vertexBuffer.Unbind(gl);
}
private static void GenerateColourBuffer(SharpGL.OpenGL gl)
{
var vertexDataBuffer = new VertexBuffer();
vertexDataBuffer.Create(gl);
vertexDataBuffer.Bind(gl);
vertexDataBuffer.SetData(gl, 1, Colores.ToArray(), false, 3);
}
private static void GenerateVertexBuffer(SharpGL.OpenGL gl)
{
var vertexDataBuffer = new VertexBuffer();
vertexDataBuffer.Create(gl);
vertexDataBuffer.Bind(gl);
vertexDataBuffer.SetData(gl, 0, Vertices.ToArray(), true, 3);
}
public static void Render(SharpGL.OpenGL gl)
{
m_vertexBuffer.Bind(gl);
gl.DrawArrays(SharpGL.OpenGL.GL_QUADS, 0, Vertices.Count);
m_vertexBuffer.Unbind(gl);
}
#region Cube Part
public static List<VertexV2> Vertexs;
public static void GenerateVertex()
{
float value = 2 / 2.0f;
Vertexs = new List<VertexV2>();
Vertexs.Add(new VertexV2(-value, value, value)); //V1
Vertexs.Add(new VertexV2(value, value, value)); //V2
Vertexs.Add(new VertexV2(value, -value, value)); //V3
Vertexs.Add(new VertexV2(-value, -value, value)); //V4
Vertexs.Add(new VertexV2(-value, value, -value)); //V5
Vertexs.Add(new VertexV2(value, value, -value)); //V6
Vertexs.Add(new VertexV2(value, -value, -value)); //V7
Vertexs.Add(new VertexV2(-value, -value, -value)); //V8
}
public static void AddVertex(int i, float x_Trans, float y_Trans, float z_Trans, Color color)
{
Vertices.Add(Vertexs[i].X + x_Trans);
Vertices.Add(Vertexs[i].Y + y_Trans);
Vertices.Add(Vertexs[i].Z + z_Trans);
Colores.Add(color.ScR);
Colores.Add(color.ScG);
Colores.Add(color.ScB);
}
public static void AddCubeValues(float x_Trans, float y_Trans, float z_Trans, Color color)
{
AddVertex(0, x_Trans,y_Trans,z_Trans, color); //V1
AddVertex(1, x_Trans,y_Trans,z_Trans, color); //V2
AddVertex(2, x_Trans,y_Trans,z_Trans, color); //V3
AddVertex(3, x_Trans, y_Trans, z_Trans, color); //V4
AddVertex(4, x_Trans,y_Trans,z_Trans, color); //V5
AddVertex(5, x_Trans,y_Trans,z_Trans, color); //V6
AddVertex(6, x_Trans,y_Trans,z_Trans, color); //V7
AddVertex(7, x_Trans,y_Trans,z_Trans, color); //V8
AddVertex(0, x_Trans,y_Trans,z_Trans, color); //V1
AddVertex(3, x_Trans,y_Trans,z_Trans, color); //V4
AddVertex(7, x_Trans,y_Trans,z_Trans, color); //V5
AddVertex(4, x_Trans,y_Trans,z_Trans, color); //V8
AddVertex(0, x_Trans,y_Trans,z_Trans, color); //V1
AddVertex(1, x_Trans,y_Trans,z_Trans, color); //V4
AddVertex(5, x_Trans,y_Trans,z_Trans, color); //V5
AddVertex(4, x_Trans,y_Trans,z_Trans, color); //V8
AddVertex(1, x_Trans,y_Trans,z_Trans, color); //V1
AddVertex(2, x_Trans,y_Trans,z_Trans, color); //V4
AddVertex(6, x_Trans,y_Trans,z_Trans, color); //V5
AddVertex(5, x_Trans,y_Trans,z_Trans, color); //V8
AddVertex(2, x_Trans,y_Trans,z_Trans, color); //V3
AddVertex(3, x_Trans,y_Trans,z_Trans, color); //V4
AddVertex(7, x_Trans,y_Trans,z_Trans, color); //V8
AddVertex(6, x_Trans, y_Trans, z_Trans, color); //V7
}
}
This is.. disastrous. Almost completely hopelessly so. But I'll give a shot at explaining some of the problems (it's impossible to detail all the problems with your code, there's so incredibly many).
Stop using the fixed pipeline, your random glMatrixMode and whatnot calls do literally nothing. Set up uniforms in your shaders and pass the projection and world (and view) transform matrices, then use them to transform your vertices.
You have a random glEnableVertexAttribArray in your render function, why? Set up your vertex buffer once and set it up correctly.
When you initialize OpenGL, request a specific version and a specific context type. There's no reason why you wouldn't set it as a 4.6 core context, but who knows what you have?
Detach and delete compiled shaders from your program, you're leaking memory. And for god's sake, check your error codes, because:
Your vertex shader has an invalid #version pre-processor instruction at the beginning, you wrote ##version. Check your error messages! And use a modern OpenGL version context, like #version 460 core.
glUseProgram(0) is undefined behaviour. Just write correct code instead and use DSA to not pollute your OpenGL state machine.
Your vertex buffer is completely wrong, you initialize it twice and set up once the position argument and once the color argument, and at the end your vertex buffer only knows about the color argument. Set it up correctly. What are those two lists doing? Data in OpenGL buffers is laid out interleaved, not in different buffers or one after the other or whatever else you imagine is happening here.
Don't use GL_QUADS, triangulate your data correctly, set up an index buffer and use GL_TRIANGLES.
Everything related to creating the cube vertices, just delete it. You do not understand how cubes work or how lists work (you randomly re-initialize your Vertexs every call, and by the way it's spelled vertexes or vertices), or again how OpenGL buffers work.
Lastly, instantiating an OpenGL context in a Win32 island in a WPF app is extremely inefficient. Both OpenGL and D3d (which is what WPF uses) provide functions to interop with eachother. Hook into WPF's D3D context and render directly into it. And don't use SharpGL, use a modern, portable (.Net5) OpenGL binding library, like OpenTK.
I just started to learn XNA/MonoGame and I encountered a weird exception.
The error says: The method or operation is not implemented.
And what's even more weird that a very similar code, almost the same, works. The only difference is that the other code is run on XNA and on another computer, and my code runs on MonoGame.
The code is supposed to make an animation from a sprite sheet.
Inside of my class, Animate:
public void set_state(string name, string state)
{
this.name = name;
this.state = state;
}
public void animate(int frameIndex)
{
this.frameIndex = frameIndex;
this.animatedTexture = Game1.contentManager.Load<Texture2D>(name + '/' + state);
prepare_frames();
while (this.frameIndex > rectangles.Count )
{
frameIndex = frameIndex - rectangles.Count;
}
base.draw(animatedTexture, rectangles[frameIndex], origins[frameIndex]);
}
public void prepare_frames()
{
find_dots();
find_rectangles();
find_origins();
}
public void find_dots()
{
cols = new Color[animatedTexture.Width];
lowestRectangle = new Rectangle(0, animatedTexture.Height - 1, animatedTexture.Width, 1);
animatedTexture.GetData<Color>(0, lowestRectangle, cols, 0, animatedTexture.Width);
for (int i = 0; i < cols.Length; i++)
{
if (cols[i] == Color.Black)
{
dots.Add(new Vector2(i, animatedTexture.Height));
}
}
}
public void find_rectangles()
{
for (int i = 0; i < dots.Count-2; i+=2)
{
rectangles.Add(new Rectangle((int)dots[i].X, 0, (int)dots[i+2].X - (int)dots[i].X, animatedTexture.Height-1));
}
}
public void find_origins()
{
for (int i = 1; i < dots.Count; i++)
{
if (i%2 != 0)
{
origins.Add(dots[i]);
}
}
}
the idea behind is that there is a line of dots below the sprite sheet, with those dots i can make frames from the sprite sheet.
Here is the data of the class Animated:
#region data
Texture2D animatedTexture;
string name, state; // to determine the animated state.
Rectangle lowestRectangle; // is the rectangle of the dot's.
Color[] cols; // this array is for the colors on the dot's rectungle.
List<Vector2> dots = new List<Vector2>(); // this list is for the dot's coordinates on the dot's rectungle.
List<Rectangle> rectangles = new List<Rectangle>(); // this list is for the new rectungles, each rectungle is a diffirent frame from the sprite sheet.
List<Vector2> origins = new List<Vector2>(); // this list is for each origin point of the new retungles.
int frameIndex;
#endregion
Here is the part that summons the methods above, in the main class of the MonoGame, Game1:
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
if (Keyboard.GetState().IsKeyDown(Keys.Right))
{
player.set_state("moshe", "run");
player.animate(frameIndex);
frameIndex++;
}
else
player.draw();
spriteBatch.End();
base.Draw(gameTime);
}
So the error occurs in this line:
animatedTexture.GetData<Color>(0, lowestRectangle, cols, 0, animatedTexture.Width);
in the method animate in the class Animate. ( The method or operation is not implemented.) When I press the right key. Why does that happens?
Quite honestly that is a big difference, between XNA and a port of it.
A NotImplementedException does exactly what it suggest, it is thrown when a method or operation has not been implemented.
Nothing appears to be documented much about this, but it has been reported to the MonoGame bug tracker and is assigned to a developer for the 3.x release.
Using the SharpDX version however, this error does not exist.
I want to create a low resolution game on a larger window. (96x54 res on 960x540 size window for example).
How would I go about this? Is there a way to resize a window independently of the preferred back buffer width and height? Or should I just keep a low resolution render target I draw on, and just draw it as a full screen quad on my window when I'm done adjusting for nearest texture sampling?
Thanks in advance,
xoorath
I tend to opt for the "render to texture" solution so that I can allow for things like full screen without distortions.
The class I use to achieve this usually looks something like:
class VirtualScreen
{
public readonly int VirtualWidth;
public readonly int VirtualHeight;
public readonly float VirtualAspectRatio;
private GraphicsDevice graphicsDevice;
private RenderTarget2D screen;
public VirtualScreen(int virtualWidth, int virtualHeight, GraphicsDevice graphicsDevice)
{
VirtualWidth = virtualWidth;
VirtualHeight = virtualHeight;
VirtualAspectRatio = (float)(virtualWidth) / (float)(virtualHeight);
this.graphicsDevice = graphicsDevice;
screen = new RenderTarget2D(graphicsDevice, virtualWidth, virtualHeight, false, graphicsDevice.PresentationParameters.BackBufferFormat, graphicsDevice.PresentationParameters.DepthStencilFormat, graphicsDevice.PresentationParameters.MultiSampleCount, RenderTargetUsage.DiscardContents);
}
private bool areaIsDirty = true;
public void PhysicalResolutionChanged()
{
areaIsDirty = true;
}
private Rectangle area;
public void Update()
{
if (!areaIsDirty)
{
return;
}
areaIsDirty = false;
var physicalWidth = graphicsDevice.Viewport.Width;
var physicalHeight = graphicsDevice.Viewport.Height;
var physicalAspectRatio = graphicsDevice.Viewport.AspectRatio;
if ((int)(physicalAspectRatio * 10) == (int)(VirtualAspectRatio * 10))
{
area = new Rectangle(0, 0, physicalWidth, physicalHeight);
return;
}
if (VirtualAspectRatio > physicalAspectRatio)
{
var scaling = (float)physicalWidth / (float)VirtualWidth;
var width = (float)(VirtualWidth) * scaling;
var height = (float)(VirtualHeight) * scaling;
var borderSize = (int)((physicalHeight - height) / 2);
area = new Rectangle(0, borderSize, (int)width, (int)height);
}
else
{
var scaling = (float)physicalHeight / (float)VirtualHeight;
var width = (float)(VirtualWidth) * scaling;
var height = (float)(VirtualHeight) * scaling;
var borderSize = (int)((physicalWidth - width) / 2);
area = new Rectangle(borderSize, 0, (int)width, (int)height);
}
}
public void BeginCapture()
{
graphicsDevice.SetRenderTarget(screen);
}
public void EndCapture()
{
graphicsDevice.SetRenderTarget(null);
}
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(screen, area, Color.White);
}
}
And then in my game, the initialisation tends look something like:
VirtualScreen virtualScreen;
protected override void Initialize()
{
virtualScreen = new VirtualScreen(96, 54, GraphicsDevice);
Window.ClientSizeChanged += new EventHandler<EventArgs>(Window_ClientSizeChanged);
Window.AllowUserResizing = true;
base.Initialize();
}
void Window_ClientSizeChanged(object sender, EventArgs e)
{
virtualScreen.PhysicalResolutionChanged();
}
With the all important call to Update:
protected override void Update(GameTime gameTime)
{
virtualScreen.Update();
base.Update(gameTime);
}
And then the act of drawing itself:
protected override void Draw(GameTime gameTime)
{
virtualScreen.BeginCapture();
GraphicsDevice.Clear(Color.CornflowerBlue);
// game rendering happens here...
virtualScreen.EndCapture();
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin();
virtualScreen.Draw(spriteBatch);
spriteBatch.End();
base.Draw(gameTime);
}
With this in place, I can basically stop caring about resolution at all and just focus on the game.
Using the RenderToTexture method you're talking about might be a good idea (plus it will be easier for you if you want to do post-process shaders).
Alternatively, you can set the Window's size, but your code will only work on a desktop.
You must add those 2 references in your project:
using System.Drawing;
using System.Windows.Forms;
And then in your Game class (i.e. in the Initialize method)
GraphicsDeviceManager.PreferredBackBufferWidth = 96;
GraphicsDeviceManager.PreferredBackBufferHeight = 54;
IntPtr ptr = this.Window.Handle;
Form form = (Form) Control.FromHandle(ptr);
form.Size = new Size(960, 540);