I am generating many cubes during runtime and applying some photos to them (it's a type of VR photo browser). Anyway, I am trying to encapsulate them all in a larger cube using the smallest possible bounding box of all the little cubes. This is what I have so far.
After generating all the cubes, the first thing I do is center them all on their parent so the user can position and rotate them relative to the center.
Transform[] children = GetComponentsInChildren<Transform>();
Vector3 newPosition = Vector3.zero;
foreach (var child in children)
{
newPosition += child.position;
child.parent = null;
}
newPosition /= children.Length;
gameObject.transform.position = newPosition;
foreach (var child in children)
{
child.parent = gameObject.transform;
}
Then I calculate the bounds of all the cubes which are children of the parent.
Renderer[] meshes = GetComponentsInChildren<Renderer>();
Bounds bounds = new Bounds(transform.position, Vector3.zero);
foreach (Renderer mesh in meshes)
{
bounds.Encapsulate(mesh.bounds);
}
Bounds maximumScrollBounds = bounds;
Then I create the new larger (red/transparent) cube which will encapsulate all the smaller ones and use the bounds I previously calculated for it's scale.
GameObject boundingBox = GameObject.CreatePrimitive(PrimitiveType.Cube);
Destroy(boundingBox.GetComponent<Collider>());
boundingBox.GetComponent<Renderer>().material = Resources.Load("Materials/BasicTransparent") as Material;
boundingBox.GetComponent<Renderer>().material.color = new Color32(255, 0, 0, 130);
boundingBox.name = "BoundingBox";
boundingBox.transform.localScale = new Vector3(maximumScrollBounds.size.x, maximumScrollBounds.size.y, maximumScrollBounds.size.z);
boundingBox.transform.localPosition = maximumScrollBounds.center;
boundingBox.transform.SetParent(transform);
But the problem is the encapsulating cube is always slightly too big and I cannot figure out why.
I need it to basically only reach the very edges of the smaller cubes inside of it.
Things I would try:
1.- I believe Bounds.Encapsulate retrieves the center in world space. So I think that:
boundingBox.transform.localPosition = maximumScrollBounds.center;
Needs to be subtituted by:
boundingBox.transform.position = maximumScrollBounds.center;
2.- Its the same, but just to give ideas to try with local/global space, I would also try:
boundingBox.transform.localPosition = boundingBox.transform.InverseTransformPoint(maximumScrollBounds.center);
On the other hand I would give a look and check all the faces of the bounding box to check if the over extension of the volume is simmetrical. Also draw the center, to try to narrow the problem and check if its more in the extents or just in the center.
As far as I can guess it is a misplaced center of the obtained bounding box, but for that to be, the exceeding volume in one side should be lacking in the other, so if that is not the problem, that could be an interesting update to move on towards the solution.
I have a list of region borders inside SQL database and i am using sharpmap to render thumbnail images for every country i need. It works really well.
But I would like to go one step further and add a small globe around it and position country on it's place in globe, but I do not know where to start.
Here's the code i'm using so far to render country thumbs. Any ideas?
var map = new Map(new Size(command.Width, command.Height));
map.BackColor = Color.Transparent;
var countryGeometry = GeometryFromWKT.Parse(command.CountryLevelWkt);
IProvider countryProvider = new GeometryFeatureProvider(countryGeometry);
var countryLayer = new VectorLayer("country", countryProvider);
var borderColor = System.Drawing.ColorTranslator.FromHtml(command.BorderColor);
countryLayer.Style.EnableOutline = true;
countryLayer.Style.Outline = new Pen(borderColor);
countryLayer.Style.Outline.Width = command.BorderWidth;
countryLayer.Style.Fill = Brushes.Transparent;
var transformationFactory = new CoordinateTransformationFactory();
countryLayer.CoordinateTransformation = transformationFactory.CreateFromCoordinateSystems(
GeographicCoordinateSystem.WGS84,
ProjectedCoordinateSystem.WebMercator);
map.Layers.Add(countryLayer);
var bottomLeft = new Coordinate(command.Extents.BottomLeft.Longitude, command.Extents.BottomLeft.Latitude);
var topRight = new Coordinate(command.Extents.TopRight.Longitude, command.Extents.TopRight.Latitude);
// transformations
var bottomLeftLongLat = countryLayer.CoordinateTransformation.MathTransform.Transform(bottomLeft);
var topRightLongLat = countryLayer.CoordinateTransformation.MathTransform.Transform(topRight);
map.ZoomToBox(new Envelope(bottomLeftLongLat, topRightLongLat));
var img = map.GetMap();
return img;
Start by drawing all countries on a new map, each on its own layer.
Draw the country in which you're interested on its own layer.
Set map center to the Envelope.Center of the layer from Step 2. Eg, if drawing Australia, the map will move to the left.
Render map as image. Draw image on a drawing sufrace (System.Drawing.Graphics).
Re-center the map, to cover the empty space. Eg, if drawing Australia, move map almost all the way to the right. You will need to work out these offsets programmatically.
Render map from step 5 as image. Add image to the same drawing sufrace (see step 4).
Repeat steps 5-6 covering empty space below/above the render made at step 3.
Here is an example:
Note that:
Australia is in the center
There is a gap between map layers near the mouse pointer (gap in screenshot is intentional to demonstrate the logic)
Some countries are very large (eg Russia) and obtaining Envelope.Center will not work very well - consider centering based on the largest polygon only
Here's a sample Windows Forms project. In the sample, I used maps from http://thematicmapping.org/downloads/world_borders.php.
I have created a scene in 3DS Max with a stock engine model and some things i added my self, the plane, and the button.
http://i.stack.imgur.com/R2iva.png
Regardless of how i export the scene, whether its as a .X using panda exporter or .fbx using 2012.2 fbx exporter both, when loaded into XNA and rendered, all appear on top of each other.
http://i.stack.imgur.com/6gdMb.png
Since the individual parts of the engine all remain where they should (and are seperate in 3ds max) im pretty sure there is something im not setting correctly in 3ds max with the layout of the rest of my objects.
Update 1 : The code i use to load the models in xna is as follows
Matrix[] transforms = new Matrix[model.Bones.Count];
model.CopyAbsoluteBoneTransformsTo(transforms);
foreach (ModelMesh mesh in model.Meshes)
{
foreach (BasicEffect be in mesh.Effects)
{
be.EnableDefaultLighting();
be.Projection = camera.projection;
be.View = camera.view;
be.World = GetWorld() * mesh.ParentBone.Transform;
// adding the additional * transforms[i]; didnt do anything
}
mesh.Draw();
}
This code works great for other peoples models but not any that i make. Its like 3ds max isnt exporting out the positions of the objects that i create in the scene relative to the scenes origin.
You need to combine all the transform matrices from parent bones and child bones like this:
Matrix[] transforms = new Matrix[model.Bones.Count];
model.CopyAbsoluteBoneTransformsTo(transforms);
foreach (ModelMesh mesh in model.Meshes) {
foreach (BasicEffect ef in mesh.Effects) {
ef.World = transforms[mesh.ParentBone.Index];
//Also do other stuff here, set projection and view matrices
}
}
There is probably a better way, but this code should work.
I can paint Model3DGroup in the color i want by using
Material material = new DiffuseMaterial(new SolidColorBrush(Colors.White));
GeometryModel3D model = new GeometryModel3D(mesh, material);
(following this tutorial)
to set the color of GeometryModel3D i include in the Model3DGroup.
I have an application where I have to place a map on a terrain in 3D (terrain is completely flat), the map is not an image, and I have detailed data of the points of the shapes I want to draw, and I also have DrawingVisual objects of all the shapes that I want to project on the 3D surface. In 2D mode, I draw them on a custom canvas (derived from Canvas) where I add them using
myCanvas.addVisualChild(item);
myCanvas.addLogicvalChild(item);
My question is how to "paint" or draw shapes and lines etc on 3D items? These shapes do not cover the terrain fully. I have tried using Viewport2DVisual3D class and tried placing a canvas on a 3D surface (a simple canvas with a button) using the following code:
Canvas testCanvas = new Canvas();
testCanvas.Background = Brushes.Green;
Button b1 = new Button();
b1.Content = "Hello there";
Canvas.SetTop(b1, 50);
Canvas.SetLeft(b1, 50);
Canvas.SetZIndex(b1, 2);
testCanvas.Children.Add(b1);
testVisual3d.Visual = testCanvas; // testVisual3d is a Viewport2DVisual3D declared in xaml
But the problem is that I am not able to figure out how the Canvas or any Visual "fills" the Viewport2DVisual3D class because:
The button filled the canvas completely.
Empty areas of the canvas (Canvas.SetTop(b1, 50)) is not visible.
I have no idea idea about the size of the canvas related to the size of Viewport2DVisual3D object because the button always fills the object completely.
Also i cannot use Viewport2DVisual3D everywhere, as I also have to create models of buildings etc. when I am projecting the map to a 3D terrain, so I'll have to paint areas of a building model (which will be a Model3DGroup) differently to give a realistic effects, but if i manage to project the map on a Viewport2DVisual3D, it'll solve lots of problems as i'll be able to directly project all the shapes including grid on the Viewport2DVisual3D object or terrain.
I am using .NET 4.0 and C#.
Please help.
Update
Using this code solves the initial problem of canvas size and space:
Canvas testCanvas = new Canvas();
testCanvas.Background = Brushes.Green;
Button b1 = new Button();
b1.Width = 120;
b1.Height = 25;
testCanvas.Width = 200;
testCanvas.Height = 200;
b1.Content = "Hello there";
Canvas.SetTop(b1, 50);
Canvas.SetLeft(b1, 50);
Canvas.SetZIndex(b1, 2);
testCanvas.Children.Add(b1);
testVisual3d.Visual = testCanvas;
The size of Viewport2DVisual3D is
Positions="0,0,0 0,0,30 30,0,30 30,0,0"
And the canvas resizes to fit the boundaries of the Viewport2DVisual3D, but will it work with a class derived from Canvas in which shapes have directly been added using Canvas.AddVisualChild and Canvas.AddLogicalChild, i am yet to try that.
And also the original question of painting on the Model3DGroup remains, how to do it?
This may be a bit late, but I was having issues with a very similar problem. Though the above works fine for smaller meshes, once you get into the hundreds and thousands (or more) of triangles, it becomes slow. The alternative I have implemented uses a uniform material for all meshes/collections but uses a lineargradientbrush to assign a gradient color to the material (stops and colors set as needed for what you are trying to achieve). Then by using texture coordinates, the color of a given triangle can be set (all three texture coordinates would be the same point if you want to have a solid color). This approach works far better and is more efficient for large meshes (10k and up) with only extra time required to set up the brush and assigning texture coordinates. The same/similar approach can also be used for changing the color of a triangle through hit-testing or similar whereby only the triangle's vertices (index) are provided and the source mesh (thus the index of the texture coordinates that need updating are provided)
So here's some code that I've used (with some modifications, adapt as necessary). This is in VB, porting to C# should be easy. Note that this is a copy-paste from some existing project, so some items may not be as clear as they could be... sorry for being lazy...
Private Sub RefreshRHDisplay()
'global/input data is RHTris which is a List(of Point3D())
'updates the data on the display with the available data
Dim v, f, i As Integer
'clear geometry
Group3D_RH.Children.Clear() 'is a Model3DGroup object
'define lights
Dim _L As Model3DGroup = GetLighting() 'defines the lighting
'mesh items
Dim mesh As New MeshGeometry3D
'group of items
Dim geomGroup As New Model3DGroup
'materials
Dim mat As MaterialGroup 'the foreground/front material
Dim bmat As MaterialGroup 'the background/back material
Dim MatGroup As New MaterialGroup
Dim ColBrush As Brush = New SolidColorBrush(_bodyCol) : ColBrush.Opacity = 1.0F
Dim LightBrush As Brush = New SolidColorBrush(Colors.White) : LightBrush.Opacity = 0.3
Dim _diffuseMaterial As New DiffuseMaterial(ColBrush)
Dim _specularMaterial As New SpecularMaterial(LightBrush, 300.0F)
MatGroup.Children.Add(_diffuseMaterial)
MatGroup.Children.Add(_specularMaterial)
mat = MatGroup
MatGroup = New MaterialGroup
Dim bColBrush As Brush = New SolidColorBrush(Colors.DarkGray) : bColBrush.Opacity = 1.0F
_diffuseMaterial = New DiffuseMaterial(bColBrush)
MatGroup.Children.Add(_diffuseMaterial)
MatGroup.Children.Add(_specularMaterial)
bmat = MatGroup
Dim vPts(0) As Point
'distinguish between direct (uniform color) and colorize mode
If chkColorize.IsChecked Then 'COLORIZE MODE
'Note: the gradient stop ends at 0.9, this is purely so that I can assign a special color after that.
Dim gsp As New GradientStopCollection({New GradientStop(Colors.Red, 0.0),
New GradientStop(Colors.Orange, 0.15),
New GradientStop(Colors.Yellow, 0.3),
New GradientStop(Colors.Green, 0.45),
New GradientStop(Colors.Blue, 0.6),
New GradientStop(Colors.Indigo, 0.75),
New GradientStop(Colors.Violet, 0.9)
})
Dim lBrsh As New LinearGradientBrush(gsp, New Point(0, 1), New Point(0.9, 1))
'define random locations for the textures
ReDim vPts(RHTris.Count - 1)
Dim rnd As New Random(CInt(Now.Ticks Mod Integer.MaxValue))
For v = 0 To RHTris.Count - 1
vPts(v) = New Point(rnd.NextDouble, 1)
Next
'replace the default material with the linear gradient
mat.Children.Clear()
mat.Children.Add(New DiffuseMaterial(lBrsh))
End If
'add all vertices
For v = 0 To RHTris.Count - 1
mesh.Positions.Add(RHTris.Item(v)(0))
mesh.Positions.Add(RHTris.Item(v)(1))
mesh.Positions.Add(RHTris.Item(v)(2))
Next
'add all triangles
v = 0
For f = 0 To RHTris.Count - 1
'If .isVisible Then 'true by def of _visList
Dim v0, v1, n As Vector3D
v0 = Point3D.Subtract(RHTris.Item(f)(1), RHTris.Item(f)(0))
v1 = Point3D.Subtract(RHTris.Item(f)(2), RHTris.Item(f)(0))
n = Vector3D.CrossProduct(v0, v1)
mesh.Normals.Add(n)
mesh.Normals.Add(n)
mesh.Normals.Add(n)
mesh.TriangleIndices.Add(v)
mesh.TriangleIndices.Add(v + 1)
mesh.TriangleIndices.Add(v + 2)
v += 3
Next
If chkColorize.IsChecked Then 'define the texture coordinates that define the color through the linear gradient brush
For v = 0 To RHTris.Count - 1
For i = 0 To 2
mesh.TextureCoordinates.Add(vPts(v)) 'add same three points for each location
Next
Next
End If
'body mesh has been defined, create body and add to visual
Dim geo As New GeometryModel3D
geo.Geometry = mesh
geo.Material = mat
geo.BackMaterial = bmat
'combine to group
geomGroup.Children.Add(geo)
'add to scene
Group3D_RH.Children.Add(geomGroup)
'add lights back in
Group3D_RH.Children.Add(_L)
End Sub
Some basic comments are in order: the data is in the RHTris collection which is a variable of type List(of Point3D()) which contains three locations. The color assigned to each is either a default material or a random color (if chkColorize is checked). The Sub also applies the data to the Group3D_RH which is a Model3DGroup object.
The approach uses either a solid material or a (constant) texture that has a color with a gradient (going through all the colors that are then available). The assignment of color is then to define a point (2D point) to the texture coordinates. If each point of the triangle has the same point assigned then the triangle ends up having a uniform color (based on its coordinates along the color gradient)...
Hope this at least illustrates the approach...
After a bit of experimenting, i have the answer, simple way is to set the color of each triangle while defining the mesh. In WPF, what is needed is to set the Material color when combining the triangle (MeshGeometry3D) and material while defining a new GeometryModel3D.
(from this tutorial)
Material material = new DiffuseMaterial(
new SolidColorBrush(Colors.DarkKhaki)); //Set color here
GeometryModel3D model = new GeometryModel3D(
mesh, material);
I am a beginner of 3D and I have not yet explored the concept of materials.
If someone knows another way, please post here.
I am creating a minecraft clone, and whenever I move the camera even a little bit fast there is a big tear between the chunks as shown here:
Each chunk is 32x32x32 cubes and has a single vertex buffer for each kind of cube, in case it matters. I am drawing 2D text on the screen as well, and I learned that I had to set the graphic device state for each kind of drawing. Here is how I'm drawing the cubes:
GraphicsDevice.Clear(Color.LightSkyBlue);
#region 3D
// Set the device
device.BlendState = BlendState.Opaque;
device.DepthStencilState = DepthStencilState.Default;
device.RasterizerState = RasterizerState.CullCounterClockwise;
// Go through each shader and draw the cubes of that style
lock (GeneratedChunks)
{
foreach (KeyValuePair<CubeType, BasicEffect> KVP in CubeType_Effect)
{
// Iterate through each technique in this effect
foreach (EffectPass pass in KVP.Value.CurrentTechnique.Passes)
{
// Go through each chunk in our chunk map, and pluck out the cubetype we care about
foreach (Vector3 ChunkKey in GeneratedChunks)
{
if (ChunkMap[ChunkKey].CubeType_TriangleCounts[KVP.Key] > 0)
{
pass.Apply(); // assign it to the video card
KVP.Value.View = camera.ViewMatrix;
KVP.Value.Projection = camera.ProjectionMatrix;
KVP.Value.World = worldMatrix;
device.SetVertexBuffer(ChunkMap[ChunkKey].CubeType_VertexBuffers[KVP.Key]);
device.DrawPrimitives(PrimitiveType.TriangleList, 0, ChunkMap[ChunkKey].CubeType_TriangleCounts[KVP.Key]);
}
}
}
}
}
#endregion
The world looks fine if I'm standing still. I thought this might be because I'm in windowed mode, but when I toggled full screen the problem persisted. I also assume that XNA is double buffered by itself? Or so google has told me.
I had a similar issue - I found that I had to call pass.Apply() after setting all of the Effect's parameters...
The fix so far has been to use 1 giant vertex buffer. I don't like it, but that's all that seems to work.