Currently I'm writing a plugin for my company and we want to offset some math operations on meshes to C# to gain some speed in calculations. I'm not only a C# beginner (I'm a Python / Maxscript guy) but I also struggle to find good SDK documentation for 3DS Max.
My current problem: obtaining the world-relative position of a vertex in a mesh. Sounds easy but it poses some problems for me. I can get the vert position by using:
IPoint3 x = mesh.GetVert(vertID);
and for vert 3 which is in [0,0,0] of the scene it returns a Point3 value of [-23,86499, 17,5783, 0], probably relative to... well, center of the object I think. Or some local transformation matrix. The thing I want to get is the position of the vert in world space, which in this case I know is [0,0,0], as well as I can probe it with MaxScript by using:
polyOp.getVert $ 3
The simple function I'm using to poke around is:
List<IINode> nodes = nodesFromHandles(objHandles);
foreach (IINode i in nodes)
{
log(i.Name);
IObjectState iState = i.EvalWorldState(0, true);
IObject iObj = iState.Obj;
IPolyObject iPoly = (IPolyObject)iObj.ConvertToType(0, global.PolyObjectClassID);
ITriObject iTri = (ITriObject)iObj.ConvertToType(0, global.TriObjectClassID);
IMesh mesh = iTri.Mesh;
for (int vertID = 0; vertID < mesh.NumVerts; vertID++)
{
IPoint3 x = mesh.GetVert(vertID); /// RETURNS A BAD POSITION? RELATIVE TO WHAT MATRIX?
log(vertID.ToString() + ": " + x.X.ToString() + ", " + x.Y.ToString() + ", " + x.Z.ToString());
}
}
I guess I should probably do something with the object transform matrix, but I'm in the dark with that. Working on: 3ds Max 2014, MS visual Studio Community 2015 (C#), Win7x64, using Autodesk.Max.DLL. Any and all help appreciated, thank you in advance!
EDIT: (solution?)
Correct me if I'm wrong, but by doing some satanic rituals and sacrificing some of my teeth I've come up with this thing. I essentially grab the object world Transform Matrix at a given time (set to frame 0, because why not?) and then doing a point transform. Seems to work quite well for now, but I need to do more tests.
List<IINode> nodes = nodesFromHandles(objHandles);
foreach (IINode i in nodes)
{
log(i.Name);
IObjectState iState = i.EvalWorldState(0, true);
IObject iObj = iState.Obj;
ITriObject iTri = (ITriObject)iObj.ConvertToType(0, global.TriObjectClassID);
IMesh mesh = iTri.Mesh;
/// OBJECT TRANSFORM MATRIX
IInterval iTimeRange = i.GetTimeRange(0);
IMatrix3 worldTm = i.GetObjTMAfterWSM(0, iTimeRange);
for (int vertID = 0; vertID < mesh.NumVerts; vertID++)
{
IPoint3 x = mesh.GetVert(vertID); /// RETURNS A BAD POSITION? RELATIVE TO WHAT MATRIX?
IPoint3 v = worldTm.PointTransform(x);
log(vertID.ToString() + ": " + v.X.ToString() + ", " + v.Y.ToString() + ", " + v.Z.ToString());
}
}
If anybody can verify or improve this - your welcome. :)
Adding some explanations to your answer. The following line:
IPoint3 vertObjectSpace = mesh.GetVert(vertID);
Returns the vertex position in object space, which is unfortunately not mentioned in the docs. As you are interested in the positions in world space, you have to transform them using the following:
IMatrix3 tmObj2World= i.GetObjectTM(0, iTimeRange); // where i is your IINode
IPoint3 vertWorldSpace = tmObj2World.PointTransform(vertObjectSpace);
Hope that helps for your understanding.
Some notes / further reading:
Docs about GetObjectTM and GetObjTMAfterWSM (guess I would prefer to use GetObjectTM, unless you explicitly have to use GetObjTMAfterWSM):
3ds max uses the following three spaces: world, node/local and object. For some background info, see: http://help.autodesk.com/view/3DSMAX/2018/ENU/?guid=__files_GUID_3B001F21_8FE9_4663_A972_E648682A0ACD_htm
The node transform (unfortunately not the object transform) can be visualized in 3ds max with the following
Related
What I want to achieve?
I'm working on an evolutionary algorithm finding min/max of non-linear functions. I have fully functional WPF application, but there's one feature missing: 3D plots.
What is the problem?
To accomplish this I've started with free trial of ilNumerics which provide 3D data visualisation. It works completely fine with examples from documentation, but there's something what prevents me from plotting properly my own 3D graphs.
Visualising problem:
So, here is how it behaves at the moment
Those are graphs of non-linear function: x1^4+x2^4-0.62*x1^2-0.62*x2^2
Left side: Contour achieved with OxyPlot
Right side: 3D graph achieved with ilNumerics
As you can see, OxyPlot contour is completely fine and 3D graph which I'm trying to plot with exactly same data is not proper at all.
How actual (not working) solution is done?
I'm trying to visualise 3D surface using points in space. ILNumerics has class called Surface which object I have to create in order to plot my graph. It has following constructor:
public Surface(InArray<float> ZXYPositions, InArray<float> C = null, Tuple<float, float> colorsDataRange = null, Colormap colormap = null, object tag = null);
where as you can see ZXYPositions is what I actually have problem with. Before instantiating Surface object I'm creating an Array like this:
int m = 0;
for (int i = 0; i < p; ++i)
{
for (int j = 0; j < p; ++j)
{
sigma[m, 0] = (float)data[i, j];
sigma[m, 1] = (float)xy[0][i];
sigma[m, 2] = (float)xy[1][j];
m++;
}
}
where sigma[m, 0] = Z; sigma[m, 1] = X; sigma[m, 2] = Y;
And here's the problem. I cannot find any logical error in this approach.
Here is code responsible for creating object which I'm passing to ilNumerics plot panel:
var scene = new PlotCube(twoDMode: false) {
// add a surface
new Surface(sigma) {
// make thin transparent wireframes
Wireframe = { Color = Color.FromArgb(50, Color.LightGray) },
// choose a different colormap
Colormap = Colormaps.Jet,
}
};
Additionaly I want to say that sigma array is constructed properly, because I've printed out its values and they're definitely correct.
Plot only data points.
At the end I need to add, that when I'm not creating surface object and plot only data points it looks much more reasonable:
But sadly it's not what I'm looking for. I want to create a surface with this data.
Good News!
I found the answer. Oddly almost evereything was fine.. I missunderstood just one thing. When I'm passing ZXYPositions argument to surface it can actually expect only Z data from me to plot graph correctly.
What did I changed to make it work
Two first for loops now looks like that:
sigma = data;
As you can see they're no longer loops, because sigma now contains only "solution" coordinates (which are Z coords), so I need to just assign data array to sigma.
Second part, where I'm creating Surface now looks like this:
var B = ILMath.tosingle(sigma);
var scene = new PlotCube(twoDMode: false) {
// add a surface
new Surface(B) {
// make thin transparent wireframes
Wireframe = { Color = Color.FromArgb(50, Color.LightGray) },
// choose a different colormap
Colormap = Colormaps.Jet,
}
};
scene.Axes.XAxis.Max = (float)arguments[0].Maximum;
scene.Axes.XAxis.Min = (float)arguments[0].Minimum;
scene.Axes.YAxis.Max = (float)arguments[1].Maximum;
scene.Axes.YAxis.Min = (float)arguments[1].Minimum;
scene.First<PlotCube>().Rotation = Matrix4.Rotation(new Vector3(1f, 0.23f, 1), 0.7f);
Basically one thing which changed is scaling XY axes to proper values.
Final results
Here you have final results:
I'm trying to find a solution for best performance.
I need to find closet point on multisegment line (List points) to given point.
My line have thousands of points and I need to check distance to this line few times per second. So solution need to be very fast.
Right now I have something like below. It works but it is going to be slow when line have 10000+ points.
Maybe someone have idea how to make it faster?
public static float GetSqrDistXZ(Vector3 a, Vector3 b)
{
Vector3 vector = new Vector3(a.x - b.x, 0, a.z - b.z);
return vector.sqrMagnitude;
}
public static Vector3 NearestPointOnFiniteLine(Vector3 start, Vector3 end, Vector3 pnt)
{
Vector3 line = (end - start);
float len = Mathf.Sqrt(line.sqrMagnitude);
line.Normalize();
Vector3 v = pnt - start;
float d = (v.x * line.x) + (v.z * line.z);
d = Mathf.Clamp(d, 0f, len);
return start + line * d;
}
int pointsCount = line.points3.Count - 1; // line - List<Vector3> points.
float[] distances = new float[pointsCount];
for (int i = 0; i < pointsCount+1; i++) {
if (i >= 1) {
distances [i - 1] = GetSqrDistXZ (point, NearestPointOnFiniteLine (line.points3 [i - 1], line.points3 [i], point));
}
}
int minListIndexLeft = System.Array.IndexOf (distances, Mathf.Min (distances));
float minimalDistance = distances[minListIndexLeft];
Vector3 closestPoint = NearestPointOnFiniteLine (line.points3[minListIndexLeft], line.points3[minListIndexLeft+1], point);
You'll want to think about space partitioning. In this example I'm going to assume a 2D space, but this works in 3D just as well. Also there are much better solutions like BSP trees and stuff, but we'll keep it simple here.
Imagine putting a grid over your 2D space. Every segment (distance between 2 points) of your line intersects with one or more cells of that grid. What you have to do is to store the intersecting segments for every cell. If your line does not change, you can do that in one single pass on startup, or even store that information statically in an Asset.
But once you have that information, all you have to do is calculate the cell that your point is inside and then only check the line segments that intersect with that specific cell or a number of direct neighbours (see below). This makes finding the closest point lightning fast in comparison.
If you play with this idea on a piece of paper you may come across cases where this solution does not yield the closest point, because it did not consider a neighboring cell that contained a closer point. The easiest way to solve this is the following approach:
1. Find cell C, which is the cell your point is in
2. Let cellRange = 0
3. Let point B be undefined
4. Find closest point P among all segments that intersect cell C and its neighboring cells of range cellRange*
5. If B is the same as newly found point P then P is the solution. You are done.
6. Increase cellRange by 1
7. Let B = P
8. Repeat from step 4
* "neighboring cells of range cellRange" means:
cellRange 0: only cell C, no neighbours
cellRange 1: cell C and direct neighbours
cellRange 2: cell C, direct neighbours and their direct neighbours
...
This solution basically checks if increasing the search range improves the solution. As soon as increasing the range did not improve the solution, you found the closest point.
I'm trying to draw lines with offset to main line like on attachment.
I have problems with my code. It generating intersections and cusps on the lines. (attachment)
Maybe someone can help me with this code provide any working example that I can follow.
// LEFT SIDE OF MAIN LINE
int numberOfLines = 10;
float offset = 10f;
lastLinePoints = outerPoints; // outerPoint = Points from Main Line
for(int i = 0; i < numberOfLines; i++)
{
List<Vector3> tempPoints = new List<Vector3> ();
for (int k = 0; k < lastLinePoints.Count; k++) {
if (k + 1 < lastLinePoints.Count) {
Vector3 direction = lastLinePoints [k + 1] - lastLinePoints [k];
// up direction:
Vector3 up = new Vector3(0.0f, 1.0f, 0.0f);
// find right vector:
Vector3 right = Vector3.Cross(direction.normalized, up.normalized);
Vector3 newPoint = lastLinePoints [k] + (right * offset);
tempPoints.Add (newPoint);
}
}
VectorLine lineTemp = new VectorLine ("lineCurved", tempPoints, 120f / _camera2DObject.GetComponent<Camera> ().orthographicSize, LineType.Continuous);
lineTemp.Draw3D ();
lastLinePoints = tempPoints;
}
After some research I know that solution for drawing curved parallel lines can be difficult. I found also some algorithms (https://hal.inria.fr/inria-00518005/document) but this mathematics is to hard for me to make code from it.
After suggestion from #jstreet I tried CLIPPER library. Results are very good but is it possible to draw only parallel line instead closed polygon around line (like on attachment)
UPDATE
I wrote another question becouse I think that using CLIPPER for parallel lines is worth it. LINK TO question
From My Previous Experience, lot of time will be spent to solve your problem without applying a polyline curves offset algorithm,so my advice is to start implementing any of the algorithms regardless the mathematical difficulties. choose one of the published algorithm that suits exactly your case, it could be easier than implementing an algorithm for any shape.
But you can get the below link a shot
https://github.com/skyrpex/clipper
calculate parallel line parameters: you will need to calculate offset as coefficient (angle) remains the same.
calculate line intersections between neighbouring lines based on calculated values from step 1.
use splines over sequential sets of three intersection points. For splines you can use any cubic spline library, there are plenty in guthub (https://gist.github.com/dreikanter/3526685) or codeproject (http://www.codeproject.com/Articles/560163/Csharp-Cubic-Spline-Interpolation).
I have a Unity project in which I have the necessary code to create a texture anywhere from 2x2 to 512x512 and fill it with perlin noise. I am using this texture to populate a map of blocks (think minecraft) .
The map generates correctly, but Unity starts lagging with that many cubes?
This is an image of the generated map, 256x256.
Any leads in the right direction would be appreciated, I'm just stumped on how to get passed this.
This is the method to populate the map
private void GenerateMap ()
{
for (int x = 0; x < creator.resolution; x++) {
for (int z = 0; z < creator.resolution; z++) {
Object newCube = Instantiate (cube, new Vector3(x, Mathf.Round (texture.GetPixel (x, z).b * 10), z), Quaternion.identity);
newCube.name = "Cube: " + x + ", " + z;
}
}
Debug.Log ("Finished generating world");
}
Probably you should try to load them dynamically as minecraft does to not having to process all the cubes on each tick.
You cannot have that many gameobjects in scene.
Should create mesh chunks (so one mesh piece basically replaces hundreds of cubes)
Browse through the huge minecraft-thread in unity forums: (plenty of explanations and samples too)
http://forum.unity3d.com/threads/after-playing-minecraft.63149/
Or just search for unity minecraft terrain, there are many tutorials for it.
I'm trying to use the .Net Chart object to interactively define a spline function that I use to map from one range of values to another. In other words, I have a 0-4095 range (x axis) that I want to convert to a 0-100 range (y axis) using a spline. I've successfully set up a chart that plots a spline through a group of points. The user can interactively move the points to get the desired function shape. Works great.
Now...once I have the spline like the user wants, how can I (using the spline function), find the corresponding y value for any x value?
I can't seem to find a way to do that. I know that the chart object is doing the calculation somewhere since it's plotting the spline...maybe they don't provide access to that.
The alternative is to make the spline calculations myself...I don't want to go there unless absolutely necessary.
Thanks.
Bryan
You need to find the value of "t" (tension parameter) that produces the desired value of x. if you are using the range of 0 to 1 the parameter "t" value will be somewhere near to 0.5. Once you know t you can calculate the corresponding value of y. Solve a cubic equation which will generate 3 values for "t" that will result in same value of x. Check the link below.
http://algorithmist.wordpress.com/2009/09/28/cardinal-splines-part-2/
Cardinal splines specify the tangents at interior points based on the vector from previous point to subsequent point. Each tangent is parallel to this vector and some multiple of its length. For example, the tangent direction at point P1 is parallel to the vector P2 – P0, or we could simply write something like T1 = s(P2 – P0) where s is a real number.
Check this part of code below where xtarget is the input value x.
Code:
for (Double t = 0; t<=1; t += 0.01)
{
s = (1 - t) / 2;
P(t)x = s(-t3 + 2t2 – t)P1X + s(-t3 + t2)P2X + (2t3 – 3t2 + 1)P2X + s(t3 – 2t2 + t)P3X + (-2t3 + 3t2)P3X + s(t3 – t2)P4X
P(t)y = s(-t3 + 2t2 – t)P1Y + s(-t3 + t2)P2Y + (2t3 – 3t2 + 1)P2Y + s(t3 – 2t2 + t)P3Y+ (-2t3 + 3t2)P3Y + s(t3 – t2)P4Y
if(P(t)x=>xtarget)
{
return P(t)y;
}
}
The above method will give the approximate point P(t)y on the curve.