In this basic rendering test I've got it loading up data for 32 vertices from a file. When it draws it will only draw a single primitive and ignores the rest of the array. For example, if I call GL.DrawArrays(PrimitiveType.Triangles, i, 3) it will draw element i.
The array does contain much more than one triangle worth of data, as the example code here will draw a different triangle every time you click, but again only one each time it renders. If I make multiple calls to DrawArrays with a different int first parameter it will also draw an additional triangle for each call. (I'm pretty sure the purpose of putting all the data into a buffer on the GPU isn't so that you can make a zillion draw calls.)
I've tried using drawelements as an alternative to drawarrays, but no matter what I put for the parameters I get a System.AccessViolationException. All the tutorials I can find always just draw one triangle and/or use drawelements, so I haven't been able to find code samples to help me figure out what is or might be different about drawing multiple primitives.
Form1 is just a blank form with a glcontrol on it. I'm using NuGet packages OpenTK 3.1.0 and OpenTK.GLControl 3.1.0.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using OpenTK.Graphics.OpenGL4;
using OpenTK;
namespace _3dRenderingTesting
{
public partial class Form1 : Form
{
int program;
int myVAO;
int i = 0;
private void glControl1_MouseClick(object sender, MouseEventArgs e)
{
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
GL.Enable(EnableCap.DepthTest);
GL.UseProgram(program);
GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Line);
GL.DrawArrays(PrimitiveType.Triangles, i, 3);
glControl1.SwapBuffers();
i++;
}
private void Form1_Load(object sender, EventArgs e)
{
program = compileShaders();
getMeshFromFile();
}
public Form1()
{
InitializeComponent();
}
private int compileShaders()
{
string vShader;
using (System.IO.StreamReader file = new System.IO.StreamReader(#"vertexshader.txt"))
{
vShader = file.ReadToEnd();
}
string fShader = "";
using (System.IO.StreamReader file = new System.IO.StreamReader(#"fragmentshader.txt"))
{
fShader = file.ReadToEnd();
}
int vertexShader = GL.CreateShader(ShaderType.VertexShader);
GL.ShaderSource(vertexShader, vShader);
GL.CompileShader(vertexShader);
int fragmentShader = GL.CreateShader(ShaderType.FragmentShader);
GL.ShaderSource(fragmentShader, fShader);
GL.CompileShader(fragmentShader);
int program = GL.CreateProgram();
GL.AttachShader(program, vertexShader);
GL.AttachShader(program, fragmentShader);
GL.LinkProgram(program);
GL.DeleteShader(vertexShader);
GL.DeleteShader(fragmentShader);
return program;
}
private int vertBuffer;
private int vertLength;
private void getMeshFromFile()
{
List<string> fileContents = new List<string>();
List<float> fVerts = new List<float>();
List<int> fFaces = new List<int>();
System.IO.StreamReader file = new System.IO.StreamReader(#"C:\Users\abc\Desktop\32 Vertex Sphere.obj");
while (!file.EndOfStream)
{
string ts = file.ReadLine().Trim().ToLower();
//find all lines that begin with "v"
//these are vertices
if (ts.Length > 0)
{
if (ts.Substring(0, 1) == "v")
{
const string reduceMultiSpace = #"[ ]{2,}";
string[] tSplit = System.Text.RegularExpressions.Regex.Replace(ts.Replace(" ", ",").Replace("\t", ","), reduceMultiSpace, ",").Split(',');
if (tSplit.Length < 4)
{
MessageBox.Show("Vertex list failure (< 3 vertices)");
Application.Exit();
}
fVerts.Add(float.Parse(tSplit[1]));
fVerts.Add(float.Parse(tSplit[2]));
fVerts.Add(float.Parse(tSplit[3]));
}
if (ts.Substring(0, 1) == "f")
{
const string reduceMultiSpace = #"[ ]{2,}";
string[] tSplit = System.Text.RegularExpressions.Regex.Replace(ts.Replace(" ", ",").Replace("\t", ","), reduceMultiSpace, ",").Split(',');
if (tSplit.Length < 4)
{
MessageBox.Show("Face list failure (< 3 vertices)");
Application.Exit();
}
fFaces.Add(int.Parse(tSplit[1]));
fFaces.Add(int.Parse(tSplit[2]));
fFaces.Add(int.Parse(tSplit[3]));
}
}
}
file.Close();
float[] fVArray = new float[fVerts.Count];
for (int i = 0; i < fVerts.Count; i++) fVArray[i] = fVerts[i];
GL.CreateBuffers(1, out vertBuffer);
GL.BindBuffer(BufferTarget.ArrayBuffer, vertBuffer);
GL.NamedBufferStorage(vertBuffer, sizeof(float) * fVerts.Count, fVArray, BufferStorageFlags.MapWriteBit | BufferStorageFlags.MapReadBit);
vertLength = fVerts.Count;
GL.CreateVertexArrays(1, out myVAO);
GL.BindVertexArray(myVAO);
GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, 3 * sizeof(float), 0);
GL.EnableVertexAttribArray(0);
}
}
}
The vertex file:
v 0 -1 0
v -0.4924088 0.7946551 -0.3550447
v 0.5536816 0.7946597 -0.2489026
v -0.06729457 -0.74536 0.6632572
v -0.9165487 0.3333308 -0.220973
v 0.06729444 0.7453622 -0.6632546
v 0.2669053 0.3333269 0.9042426
v -0.06128498 0.7946486 0.6039683
v 0.8958825 -0.1875861 -0.4027478
v -5.442639E-06 1 9.193043E-06
v -0.6496407 -0.3333396 0.6832653
v 0.6080519 -0.7453524 -0.2733544
v -0.5536865 -0.7946557 0.2489048
v -0.8958843 0.1875851 0.4027444
v 0.4430935 -0.3333268 -0.8322027
v 0.9422514 0.3333381 -0.03236934
v -0.5407486 -0.7453555 -0.3899181
v -0.09915181 -0.1875999 0.9772283
v 0.4924095 -0.7946548 0.3550446
v -0.9422525 -0.333336 0.03236436
v 0.0612843 -0.7946532 -0.6039625
v 0.91655 -0.3333305 0.2209681
v 0.4991637 -0.3333373 0.7998261
v -0.4430951 0.3333244 0.8322028
v -0.2669008 -0.3333296 -0.9042429
v -0.7967249 -0.1875918 -0.5744899
v 0.5407484 0.7453554 0.3899185
v 0.7967286 0.1875919 0.5744848
v 0.09915482 0.1876006 -0.9772278
v 0.6496406 0.3333421 -0.6832644
v -0.6080542 0.7453504 0.2733551
v -0.4991595 0.3333374 -0.7998286
The simple v&f shaders:
#version 450 core
layout (location = 0) in vec4 position;
void main(void)
{
gl_Position = position;
}
#version 450 core
out vec4 FragColor;
void main(void)
{
FragColor = vec4(1.0,1.0,1.0,1.0);
}
The ELEMENT_ARRAY_BUFFER is stated in the Vertex Array Object, so the vertex array object has to be bound, before the element array buffer can be bound. See Index buffers
# create vertex buffer
GL.CreateBuffers(1, out vertBuffer);
GL.NamedBufferStorage(vertBuffer, sizeof(float) * fVerts.Count, fVArray,
BufferStorageFlags.MapWriteBit | BufferStorageFlags.MapReadBit);
vertLength = fVerts.Count;
# create index buffer
GL.CreateBuffers(1, out indexBuffer);
GL.NamedBufferStorage(indexBuffer, sizeof(int) * fFaces.Count, fFaces ,
BufferStorageFlags.None);
indexCount = fFaces.Count;
# bind vertex array object
GL.CreateVertexArrays(1, out myVAO);
GL.BindVertexArray(myVAO);
# specify array of generic vertex attribute data for currently bound vertex array object
GL.BindBuffer(BufferTarget.ArrayBuffer, vertBuffer);
GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, 3 * sizeof(float), 0);
GL.EnableVertexAttribArray(0);
# associate index buffer to currently bound vertex array object
GL.BindBuffer(BufferTarget.ElementArrayBuffer, indexBuffer);
Drawing:
GL.DrawElements(BeginMode.Triangles, indexCount, DrawElementsType.UnsignedInt, 0);
Related
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;
public class SavedGamesSlots : MonoBehaviour
{
public GameObject saveSlotPrefab;
public float gap;
private GameObject slots;
private string[] imagesToLoad;
private RawImageInfo rawImageInfo;
// Start is called before the first frame update
void Start()
{
rawImageInfo = GetComponent<RawImageInfo>();
string imagesFolder = Path.Combine(Application.dataPath + "/Saved Screenshots");
if (!Directory.Exists(imagesFolder))
{
Directory.CreateDirectory(imagesFolder);
}
imagesToLoad = Directory.GetFiles(imagesFolder, "*.png");
slots = GameObject.FindGameObjectWithTag("Slots Content");
if (imagesToLoad.Length > 0 && slots != null)
{
for (int i = 0; i < imagesToLoad.Length; i++)
{
var go = Instantiate(saveSlotPrefab);
go.transform.SetParent(slots.transform);
Texture2D thisTexture = new Texture2D(100, 100);
string fileName = imagesToLoad[i];
go.GetComponent<RawImageInfo>().FolderAndFileName = fileName;
rawImageInfo.FolderAndFileName = fileName;
byte[] bytes = File.ReadAllBytes(fileName);
thisTexture.LoadImage(bytes);
thisTexture.name = fileName;
go.GetComponent<RawImage>().texture = thisTexture;
var raw = go.GetComponent<RectTransform>();
raw.anchoredPosition = new Vector3(i * gap, 6, 0);
}
}
}
// Update is called once per frame
void Update()
{
}
}
This is the lines for the gaps :
var raw = go.GetComponent<RectTransform>();
raw.anchoredPosition = new Vector3(i * gap, 6, 0);
Now there is no gap between the raw images at all. There are two raw images in this case.
No gap between them and i want that the first gap will start from the left so the raw images will start from the left with a gap for example if i set gap to 3 then it will start with a gap from the left side and then 3 gap between each raw image and when getting to the end if there are more raw images start a new line under the first one with the same gaps.
The scroll view settings :
I see now that the created raw's Rect Transform is disabled and the only option that is enabled is the Z position of the raw :
screenshot of the content :
screenshot of the viewport :
screenshot of the scroll view :
You need add the width of the image.
float x = 0f;
for (int i = 0; i < imagesToLoad.Length; i++)
{
var raw = go.GetComponent<RectTransform>();
raw.anchoredPosition = new Vector3(x, 6, 0);
x += raw.sizeDelta.x + gap; // width + gap
}
So this is driving me insane. I'm trying to fill my whole picture box with color using a byte array but I can't seem to figure out where to end my fors. If I set them to the dimensions of the bmp it doesn't cover the whole thing, not even close.
Please help. Here's the code this far
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using System.Drawing.Imaging;
namespace TEST_
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
byte[] BitmapData_From_Bitmap(Bitmap bmp)
{
MemoryStream ms = new MemoryStream();
bmp.Save(ms, ImageFormat.Bmp);
return (ms.GetBuffer());
}
Bitmap Bitmap_From_BitmapData(byte[] bmpData)
{
MemoryStream ms = new MemoryStream(bmpData);
return (new Bitmap(ms));
}
void set_color(byte[] byte_array, int rezO, Color col, int l, int c)
{
byte_array[54+4 * c + 4 * l *rezO] = col.B;
byte_array[ 54+4 * c + 4 * l * rezO + 1] = col.G;
byte_array[54+4 * c + 4 * l * rezO + 2] = col.R;
byte_array[ 54+4 * c + 4 * l * rezO+3] = col.A;
}
private void p_MouseDown(object sender, MouseEventArgs e)
{
Graphics g = p.CreateGraphics();
Bitmap bmp = new Bitmap(p.Width, p.Height, g);
byte[] byte_array = BitmapData_From_Bitmap(bmp);
int rezO = Convert.ToInt32(bmp.HorizontalResolution);
for (int i = 0; i <bmp.Height; i++)
{
for (int j = 0;j<bmp.Width;j++)
{
set_color(byte_array, rezO,Color.Red, i, j);
}
}
bmp = Bitmap_From_BitmapData(byte_array);
g.DrawImage(bmp, 0, 0);
}
}
}
Your pixel-wrangling routine is all kinds of bad, but rather than pick that apart, I'll just point out that you're going about accessing the raw bytes of a bitmap in the hardest possible way.
Consider this instead:
// note use of 'using'... if you don't dispose of the bitmap
// after you've drawn it, you'll have memory leakage issues
using Bitmap bmp = new Bitmap(p.Width, p.Height, g);
// you might not need this if you know you'll always
// be using 32bpp pixel formats or whatever.
var bytesPerPixel = bmp.PixelFormat switch
{
PixelFormat.Format32bppArgb => 4,
PixelFormat.Format24bppRgb => 3,
_ => throw new NotSupportedException()
};
// BitmapData is the correct way to access the raw bytes of an image
var bits = bmp.LockBits(
new Rectangle(0, 0, bmp.Width, bmp.Height),
ImageLockMode.ReadWrite,
bmp.PixelFormat);
try
{
for (var y = 0; y < bits.Height; y++)
{
// we need a raw pointer here, and that means we need
// an unsafe block
unsafe
{
// the stride of an image can sometimes be longer than the
// number of actual pixel bytes in a row
var row = new Span<byte>(
(bits.Scan0 + y * bits.Stride).ToPointer(),
bits.Width * bytesPerPixel);
for (int p = 0; p < row.Length; p += bytesPerPixel)
{
var someColour = Color.Red;
row[p + 0] = someColour.B;
row[p + 1] = someColour.G;
row[p + 2] = someColour.R;
if (bytesPerPixel == 4)
row[p + 3] = someColour.A;
}
}
}
}
finally
{
// always unlock the bits when you're done.
bmp.UnlockBits(bits);
}
Note that:
I'm using BitmapData.Scan0 to get a pointer to the raw bytes underlying the bitmap, rather than hacking around with saving an image to a memory stream.
I'm using a Span<byte> to access the raw memory, as it is slightly less hazardous to do so than to mess about with raw pointer access and arithmetic.
I'm not using anything like HorizontalResolution. That's a value in eg. dots-per-inch, and is of no use to you here at all.
I'm using bits.Stride to know how many bytes are found in a row. This is because non-32bpp images (eg. plain 24bpp RGB) can have extra padding bytes at the end of each row that aren't part of any pixels.
I'm not creating new bitmaps... I'm modifying the one I started with.
I'm disposing of my bitmap when I'm finished with it so I don't leak memory.
If you know your image always has the same pixel format, you can do things like this:
struct PixelBgra
{
public byte B, G, R, A;
}
and create your span like this:
// note that I'm not using `bytesPerPixel` anymore,
// because I'm using a struct of the right size
var row = new Span<PixelBgra>((bits.Scan0 + y * bits.Stride).ToPointer(), bits.Width);
for (var x = 0; x < row.Length; x++)
{
row[x].R = someColour.R;
// etc
}
I tried a few possibilities but I could not get it right. On SelectionChanged, I create a plane from a selected face. This is good, I can get the plane correctly.
var item = e.AddedItems[0];
if (item is Model.SelectedFace)
{
var faceItem = ((Model.SelectedFace)item);
var ent = faceItem.Item;
if (ent is Brep)
{
var sol = (Brep)ent;
if (faceItem.ShellIndex == 0)
{
var mesh = sol.Faces[faceItem.Index].ConvertToMesh();
var plane = new Plane(mesh.Vertices[0], mesh.Vertices[1], mesh.Vertices[2]);
}
}
}
ThexyTheta, xzTheta, and yzTheta between the selected face's plane and Plane.XY, Plane.XZ, & Plane.YZ are correctly calculated. For the purpose of this question, I only show xyTheta as below (I already tried using this with Transformation Matrix but it did not work well either. Thus its purpose is just for me to check if the angle between the two planes is correct.
var x0 = plane.Equation.X; var y0 = plane.Equation.Y; var z0 = plane.Equation.Z;
var x1 = Plane.XY.Equation.X; var y1 = Plane.XY.Equation.Y; var z1 = Plane.XY.Equation.Z;
xyTheta = Math.Acos(Math.Abs(x0 * x1 + y0 * y1 + z0 * z1)
/ (Math.Sqrt(x0 * x0 + y0 * y0 + z0 * z0)
* Math.Sqrt(x1 * x1 + y1 * y1 + z1 * z1)));
My transformation transXY can only do the Translation correctly but not the Rotation. For example, after transformated, my object still has 10 degree difference between plane and Plane.XY, although it gets moved.
transXY = new Transformation();
transXY.Rotation(plane, Plane.XY);
// transXZ = new Align3D(plane, Plane.XZ);
foreach (Entity ent in theModel.Entities)
{
ent.TransformBy(transXY);
}
plane.AxisX,Y,Z seem to work for the transformation matrix. **Your BREP has to be transformed back to the origin for this to work, hence the transforming it by the inverse of the previous to move it back to origin.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using devDept.Eyeshot;
using devDept.Eyeshot.Entities;
using devDept.Geometry;
namespace EyeshotTest
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
makeSquare();
}
Solid3D square, square2;
Transformation trans2 = new Transformation(1);
int i = 0;
private void timer1_Tick(object sender, EventArgs e)
{
Mesh faceMesh = square2.Faces[i].ConvertToMesh();
var plane = new Plane(faceMesh.Vertices[0], faceMesh.Vertices[1], faceMesh.Vertices[2]);
Point3D origin = plane.Origin;
Vector3D xVec = plane.AxisX;
Vector3D yVec = plane.AxisY;
Vector3D zVec = plane.AxisZ;
trans2.Invert();
square.TransformBy(trans2);
trans2 = new Transformation(origin, xVec, yVec, zVec);
square.TransformBy(trans2);
viewportLayout1.Entities.Regen();
viewportLayout1.Invalidate();
i++;
if(i == 4) { i = 0; }
}
private void makeSquare()
{
square = Solid3D.CreateBox(5, 5, 5, 0.01);
square2 = Solid3D.CreateBox(5, 5, 5, 0.01);
viewportLayout1.Entities.Add(square, Color.Green);
viewportLayout1.Entities.Add(square2, Color.FromArgb(75, Color.Blue));
Transformation trans = new Transformation(new double[,]{
{-0.17,0.67,0.72, 47.33 },
{0.28,-0.67,0.69, 10.24 },
{0.95,0.32,-0.07, 15.98 },
{0,0,0,1 } });
square2.TransformBy(trans);
viewportLayout1.Invalidate();
}
}
}
Cheers!
Works now or not???
ent.TransformBy(transformation); This is wrong.
ent.TransformBy(transXY); This is right if enything else is ok.
Regards
I'm trying to build a game 'blocks' which sees the matches cubes and delete them.
This code makes two cubes and moves them along the y-axis. Then, it supposed to disappear and 100 cube in different places to appear. My problem is I don't know how to make the cubes look disappearing nor how to write the function 'cube generation' , I'm writing in 3d.
Any help of what should I do.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Tao.OpenGl;
using System.Threading;
using Tao.FreeGlut;
namespace TAOSample
{
class VertexDania{
double vx, vy , vz;
public double X{
get{return vx;}
set{ vx = value;}
}
public double Y{
get{return vy;}
set{ vy = value;}
}
public double Z{
get{return vz;}
set{ vz = value;}
}
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
simpleOpenGlControl1.InitializeContexts();
Gl.glClearColor(1, 1, 0, 0);
Gl.glClearDepth(1.0);
Gl.glMatrixMode(Gl.GL_PROJECTION);
Gl.glLoadIdentity();
Glu.gluPerspective(45, simpleOpenGlControl1.Height /(double)simpleOpenGlControl1.Width, 0.1, 1000);
Gl.glViewport(0, 0, simpleOpenGlControl1.Width, simpleOpenGlControl1.Height);
Tao.FreeGlut.Glut.glutInit();
}
private void simpleOpenGlControl1_Paint(object sender, PaintEventArgs e)
{
Gl.glEnable(Gl.GL_LIGHTING);
Gl.glEnable(Gl.GL_LIGHT0);
Gl.glEnable(Gl.GL_COLOR_MATERIAL);
Gl.glEnable(Gl.GL_DEPTH_TEST);
Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT);
Gl.glLoadIdentity();
VertexDania[] points = new VertexDania[]
{
new VertexDania{X=0 , Y=0.73 , Z=-0.75},
new VertexDania{X=0.1 , Y=0.85 , Z=-0.75},
new VertexDania{X=-0.01 , Y=0.95 , Z=-0.75},
new VertexDania{X=-0.1 , Y=0.80 , Z=-0.75},
new VertexDania{X=0.1 , Y=0.65 , Z=-1},
new VertexDania{X=0 , Y=0.5 , Z=-0.75},
new VertexDania{X=-0.1 , Y=0.6 , Z=-1}
};
VertexDania[] point = new VertexDania[]
{
new VertexDania{X=0, Y=0.23, Z=-0.75},
new VertexDania{X=0.1 , Y=0.35 , Z=-0.75},
new VertexDania{X=-0.01 , Y=0.45 , Z=-0.75},
new VertexDania{X=-0.1 , Y=0.30 , Z=-0.75},
new VertexDania{X=0.1 , Y=0.1 , Z=-0.75},
new VertexDania{X=0 , Y=0.0 , Z=-0.75},
new VertexDania{X=-0.1, Y=0.1, Z=-0.75}
};
double[,] normals = new double[,]
{
{0,0,1},
{0,0,-1},
{0,0,-1}
};
int[,] faces = new int[,]
{
{0 , 1 ,4 , 5},
{0 , 3 , 6 , 5},
{0, 3 , 2 , 1}
};
Gl.glColor3d(1, 0, 0);
Gl.glTranslated(0, ymove, 0.1);
for (int i = 0; i < faces.GetLength(0); i++)
{
Gl.glNormal3d(normals[i, 0], normals[i, 1], normals[i, 2]);
Gl.glBegin(Gl.GL_QUADS);
for (int j = 0; j < 4; j++)
{
Gl.glVertex3d(points[faces[i, j]].X, points[faces[i, j]].Y, points[faces[i, j]].Z);
}
Gl.glEnd();
Gl.glNormal3d(normals[i, 0], normals[i, 1], normals[i, 2]);
Gl.glBegin(Gl.GL_QUADS);
for (int j = 0; j < 4; j++)
{
Gl.glVertex3d(point[faces[i, j]].X, point[faces[i, j]].Y, point[faces[i, j]].Z);
}
Gl.glEnd();
}
if (ymove >= -3)
ymove -= 0.03;
}
void redrawThread()
{
while (true)
{
Thread.Sleep(50);
simpleOpenGlControl1.Invoke(new Action(delegate()
{
simpleOpenGlControl1.Draw();
}));
}
}
private void Form1_Load(object sender, EventArgs e)
{
Thread t = new Thread(redrawThread);
t.IsBackground = true;
t.Start();
}
private void simpleOpenGlControl1_Resize(object sender, EventArgs e)
{
Gl.glMatrixMode(Gl.GL_PROJECTION);
Gl.glLoadIdentity();
Glu.gluPerspective(45, simpleOpenGlControl1.Width / (double)simpleOpenGlControl1.Height, 0.1, 100);
Gl.glViewport(0, 0, simpleOpenGlControl1.Width, simpleOpenGlControl1.Height);
// Gl.glMatrixMode(Gl.GL_MODELVIEW);
}
}
}
This is about data structure / organization than anything else. Since you know how to draw a cube you can start by making a Cube class. This is fairly trivial with immediate mode OpenGL (what you are using)
Since you know how to draw a cube, this class should be fairly easy :
class Cube
{
// Member variables
x,y,z position
r,g,b color
// Functions
public Cube(position, color); // Constructor
public draw(); // draw the cube
}
Then you need a structure that keeps the information about what you should be drawing.
class CubeGroup
{
// Member variables
private Cube[] cubes; // cube array
// Member functions
public AddCube(Cube in_cube); // Adds a cube
public DeleteCube(int index); // Removes a cube
public draw(); // Draws this group of cubes
....
}
You need at least some structures to help you achieving your goal. This makes you able to at least manage one set of cubes. You need to carefully think about what operations you want to do on your data and make structures that makes sense for this. It seems you want your data to change over time as well. Then you need some other structure to deal with that.
This is just pure speculation at this point, but hopefully it might lead you in the right direction. The classes here are just examples and in no way meant to be "the solution".
I have an application which I'm interested in eventually porting to mono so I'm trying to avoid using p/invoke's to accomplish this task.
I would like to load a cursor dynamically, as in I have a Bitmap that is generated on the fly in the application. From what I can tell the safest way to do it without using p/invoke's is to create a .cur file which I can then load to a memory stream and use the Cursor(Stream) constructor. However I have no idea how to create a .cur file.
I found this article on the Microsoft Knowledge Base which sort of explains the format, but I'm not sure how it can be used without the interop calls. How To Create an Alpha Blended Cursor or Icon in Windows XP
Does anyone else have a managed solution I can use to accomplish this task?
I was reference the post:
How can i replace cursor with bitmap in winform
You can create a Array of static cursor and use Timer to change it
to make dynamic mouse cursor effect!
Create static cursor from bitmap is so simply and without using interop:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Icon icon = this.Icon;
Bitmap bmp = icon.ToBitmap();
Cursor cur = new Cursor(bmp.GetHicon());
this.Cursor = cur;
}
}
Here's an example in VB.NET of creating a bitmap on the fly and turning it into a cursor. Not sure who this will help 7+ years on, but here goes:
Private Function GetCircleCursor(iDiameter As Integer) As Cursor
Dim oBitmap As Bitmap = New Bitmap(Convert.ToInt32(iDiameter), Convert.ToInt32(iDiameter), System.Drawing.Imaging.PixelFormat.Format32bppArgb)
Using g As System.Drawing.Graphics = Graphics.FromImage(oBitmap)
g.Clear(Color.Transparent)
g.DrawEllipse(New System.Drawing.Pen(Color.White, 3), New Rectangle(0, 0, iDiameter, iDiameter))
g.DrawEllipse(New System.Drawing.Pen(Color.Black, 1), New Rectangle(0, 0, iDiameter, iDiameter))
End Using
Return New Cursor(oBitmap.GetHicon)
End Function
The code below creates a stream filled with data in .cur format, which I succesfully tested in Ubuntu with Mono. However .NET without using Win32 API only supports black and white colors in custom cursor:
The Cursor class does not support animated cursors (.ani files) or cursors with colors other than black and white.
Therefore in Unix with Mono one is rewarded with a cursor which
displays black and white colors only
has no semi-transparent pixels, only fully opaque / fully transparent ones
¯\_(ツ)_/¯
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace StackoverflowExample
{
public static class CursorHelper
{
public static Cursor CreateCursor(Bitmap bmp, Point hotSpot)
{
// https://en.wikipedia.org/wiki/ICO_(file_format)
var bmpData = bmp.LockBits(new Rectangle(default, bmp.Size), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
try
{
int numBytes = bmpData.Stride * bmpData.Height;
var bgraValues = new byte[numBytes];
Marshal.Copy(bmpData.Scan0, bgraValues, 0, numBytes);
int max = Math.Max(bmp.Width, bmp.Height);
if (max > 256)
throw new NotSupportedException();
byte iconSizeByte = _sizes.FirstOrDefault(s => s >= max); // 0 means 256
int iconSizeI = iconSizeByte == 0 ? 256 : iconSizeByte;
const int bytesPerPixel = 4;
const int bytesPerPixelSource = 4;
byte[] emptyPixel = new byte[bytesPerPixel];
using (var stream = new MemoryStream())
using (var writer = new BinaryWriter(stream))
{
writer.Write((ushort)0); // idReserved
writer.Write((ushort)2); // idType, 1 = .ico 2 = .cur
writer.Write((ushort)1); // idCount
writer.Write(iconSizeByte);
writer.Write(iconSizeByte);
writer.Write((byte)0); // colorCount
writer.Write((byte)0); // reserved
writer.Write((ushort)hotSpot.X);
writer.Write((ushort)hotSpot.Y);
var pixelsCount = iconSizeI * iconSizeI;
var xorLength = pixelsCount * bytesPerPixel;
var andLength = pixelsCount / 8 * 2;
writer.Write((uint)(40 + xorLength + andLength)); // sizeInBytes
writer.Write((uint)stream.Position + sizeof(uint)); // fileOffset = 22 = 0x16
writer.Write(40u); // cursorInfoHeader.biSize
writer.Write((int)iconSizeI); // cursorInfoHeader.biWidth
writer.Write((int)iconSizeI * 2); // cursorInfoHeader.biHeight
writer.Write((ushort)1); // cursorInfoHeader.biPlanes
writer.Write((ushort)(8 * bytesPerPixel)); // cursorInfoHeader.biBitCount
writer.Write(0u); // cursorInfoHeader.biCompression
writer.Write(0u); // cursorInfoHeader.biSizeImage
writer.Write(0); // cursorInfoHeader.biXPelsPerMeter;
writer.Write(0); // cursorInfoHeader.biYPelsPerMeter;
writer.Write(0u); // cursorInfoHeader.biClrUsed = binaryReader2.ReadUInt32();
writer.Write(0u); // cursorInfoHeader.biClrImportant = binaryReader2.ReadUInt32();
using (var andMask = new MemoryStream(andLength))
{
byte def = 255;
for (int j = 0; j < iconSizeI; j++)
{
int y = iconSizeI - 1 - j;
byte curByte = def;
for (int i = 0; i < iconSizeI; i++)
{
var bitIndex = 7 - i % 8;
if (i < bmp.Width && y < bmp.Height)
{
var p = y * bmpData.Stride + i * bytesPerPixelSource;
stream.Write(bgraValues, p, bytesPerPixel);
if (bgraValues[p + 3] > 0)
curByte = (byte)(curByte & ~(1 << bitIndex));
}
else
stream.Write(emptyPixel, 0, emptyPixel.Length);
if (bitIndex == 0)
{
andMask.WriteByte(curByte);
curByte = def;
}
}
}
for (int j = 0; j < iconSizeI; j++)
for (int b = 0; b < iconSizeI / 8; b++)
andMask.WriteByte(def);
andMask.Seek(0, SeekOrigin.Begin);
andMask.CopyTo(stream);
}
stream.Seek(0, SeekOrigin.Begin);
// debug
// File.WriteAllBytes("/home/kolia/Documents/stream", stream.ToArray());
// stream.Seek(0, SeekOrigin.Begin);
var cursor = new Cursor(stream);
return cursor;
}
}
finally
{
bmp.UnlockBits(bmpData);
}
}
private static readonly byte[] _sizes = { 16, 32, 64, 128 };
}
}
Simple: YOU CAN NOT - the functionaltiy you ask for is not part of the .NET framework, so you need to go native.
If you application needds porting to mono, isloate this code in one class so you can turn if off like with a compiler switch - not hard.