Closed aqiruse closed 8 years ago
You could try using a data source plugin for the geometry node - this enables you to create the scene graph containing the geometry node, then defer the population of that geometry to when the data is actually available.
The idea is that you create a geometry node wired to get its data from a symbolic "source" (ie. a factory), then via the plugin, post your data to that source and the geometry will collect it before the next scene render.
Two patterns of usage:
http://scenejs.org/examples.html?page=geometryPluginPushStream
http://scenejs.org/examples.html?page=geometryPluginPullStream
These descriptions are maybe not that intuitive - check out those examples and hopefully it makes sense.
PS What about just doing this?
SceneJS.Types.addType("foo/bar", {
init:function (params) {
var self = this;
loadMyImage("foo.jpg",
function (image) {
self.addNode({
type: "geometry",
positions: [ /* data from image */]
});
});
}
});
PS. If you come up with a way to automatically compute the normals for a mesh, I'd be really interesting in getting that into the framework somewhere, perhaps within the grid plugin: https://github.com/xeolabs/scenejs/blob/V3.0/src/plugins/node/ground/grid.js
I have uploaded what I am trying to my fork at https://github.com/aqiruse/scenejs
The actual code I am working on is located in two files:
and https://github.com/aqiruse/scenejs/blob/V3.0/api/latest/plugins/node/heightmap/heightmap.js
I am trying the addNode technique during loadImage now, however that is not working. When you get a chance, do you think you could look at these files? Maybe you will have a better idea of what I am doing wrong.
I had a play around and got a heightmap rendering, you may need to change a path or two, but these work for me:
The heightmap plugin: https://gist.github.com/xeolabs/5855386
The HTML page: https://gist.github.com/xeolabs/5855394
Thank you for the work. It does work for me.Though I am not quite sure why a flat area and a height area are both being created when only one image is being loaded. Any ideas on that? I am now in the process of modifying this to create a terrain. When I did the points, I guess I was not sure what to expect and it does indeed create points. However they are too spread for a terrain. I am researching point to triangle algorithms for heightmaps now and it will probably take some time before I have another implementation with the desired result. I did find one, and when I attempted to adapt from opengl to webgl, I receive a giant block, lol. Will need some time for more analysis and work. Thank you for the help.
Not sure why there is the flat and height area
For triangulation, you could start with a plane geometry which already has the triangles (as in the 'plane' plugin, linked below), then sample the heightmap image for each vertex position, to obtain the height for that vertex.
https://github.com/xeolabs/scenejs/blob/V3.0/api/latest/plugins/geometry/plane.js
If the flat area problem is related to vertex index calculations, then this technique should fix that as well.
Your next problem would be determining the normals on the mesh..
I have a basic working example for the noisy image. However, the terrain does not display properly. In one direction the terrain is the correct size, however in the other dimension it appears half of what it should be, or else vice versa.
The terrain sample I have is at:
https://github.com/aqiruse/scenejs/blob/V3.0/examples/ex/customNodes/bundled/skyHeightmap.html
The heightmap.js file is at the previous link I gave for it and have updated it.
I have a new difficulty though, when I load an image that is a simple black and white image, that should react the same way as the noisy terrain image, it does not. In fact, the black and white image creates something which looks nothing like the actual image. I have tried with both an image and negative of that image with the same results. I will work on this later and try to diagnose the problem. It is getting there though. I have also updated the heightmap code to actually use the texture map. Though current I am drawing lines instead of a triangle-fan (which I would use others since it displays 'more' correctly) in order to see what is going on. The heightmap black and white image is located in the images directory on my github.
I have used a diamond-square algorithm in JavaScript from Sann-Remy Chea and adapted it to fit. I have two versions in the heightmap.js file. One which generates a totally random terrain, and another which generates the terrain using the heightmap image file for its data points then applies the diamond-square algorithm to it in order to generate the final terrain. His terrain generator was originally written for Three.js and I adapted it for SceneJS. I have added two sample files. One which shows the terrain from an image and another that shows the terrain from a random generation. I also added in Stats.js so that the frames per second of the drawing may be seen.
The files are located at:
https://github.com/aqiruse/scenejs/blob/V3.0/examples/ex/customNodes/bundled/skyHeightmap.html
https://github.com/aqiruse/scenejs/blob/V3.0/examples/ex/customNodes/bundled/skyHeightmapRandom.html
https://github.com/aqiruse/scenejs/blob/V3.0/api/latest/plugins/node/heightmap/heightmap.js
I am working on another version for drawing a building from a heightmap though I am having some trouble with it drawing the walls (which can be in either direction on the image) and generating the building from this image. I have thought today about modifying your box or quad code and adapting it to fit the needs of this type of generation. Unfortunately, the current heightmap image terrain generator does work for such an image, it looks horrible when it attempts to draw it, though the current heightmap image system looks great for a noisy image though I am curious why certain anomalies exist like a hole in the center of the map instead of a mountain peak? Regardless, it does work and I am working on a new custom plugin, I am calling building_heightmap which will specifically handle a black and white image with straight lines primarily and draw a 3D terrain based upon it.
If you have any suggestions or would like to incorporate the heightmap with the diamond-square algorithm based code, feel free.
I just tried these out, nice work!
I'll incorporate it into the plugins set at some point soon. I'll probably just give the var names some tweaking, just for style consistency.
What we really need now though is computation of the normals (unless I missed something in the code)..
I wonder if the problems you're having are something to do with the max vertex array size allowed on WebGL?
I figured out a basic technique for the height-map for a building layout. It works and is available on:
with the heightmap plugin at: https://github.com/aqiruse/scenejs/blob/V3.0/api/latest/plugins/node/building_heightmap/building_heightmap.js
However, I encountered a serious issue if a person tries to texture each block node individually with a ton of block nodes, there gets what chrome calls a "snag". I also found another error a couple times that lead back to gl calls like gl.uniform3fv and not behaving properly. All these were to do with the texturing though.
The "Snag" version is at:
I updated this working version so that it will take a gray-scale image and draw it, but two notes are that 1) it adds a plane as a floor and everything below threshold is cutoff and marked as floor, and 2) it uses a cube as the basis of the drawing, which means that each pixel in the map, becomes another cube in the building. This can cause problems due to a large map size when drawing, but overall, the frame-rate is not that bad. Need to play around more with like a "moving user" POV camera to determine if a large map will be a problem or not. Should be some way to only draw visible portions of it if required. Anyways, it does work. I do not like how jpeg results in uneven walls due to slight variations in the pixels stored in the image. I may add a feature to toggle between exact heights and 1.0 / 0.0 heights in the future. If you would like to check out either the working or non-working versions, feel free.
PS. I have some readings on automatically being able to calculate the normals, though I have not had the chance to go in depth to the readings or implement anything yet. On my todo list though.
The following code presents two versions of Calculating surface normals, the first version is for a triangle and the second version is for a polygon. Not sure if this is what you wanted, but here is it. To calculate for a single vertex, you would calculate the triangle normal and then average them for the individual vertex normal. This is code I translated from the psuedo-code at: http://www.opengl.org/wiki/Calculating_a_Surface_Normal
btw, I have not tested this yet. Just finished translating them. Need to find some known values for testing. Regardless, here they are and should work with little changes. I also have two versions of each function, one for a vector with x, y, z, and another for [0], [1], [2] values.
code:
function Normalize(vector)
{
var u = vector;
var v = vector;
var veclen = Math.sqrt(u.x * v.x + u.y * v.y + u.z * v.z);
var s = 1.0 / veclen;
var dest = [];
dest.x = v.x * s;
dest.y = v.y * s;
dest.z = v.z * s;
return dest;
}
function NormalizeNonX(vector) {
var u = vector;
var v = vector;
var veclen = Math.sqrt(u[0] * v[0] + u[1] * v[1] + u[2] * v[2]);
var s = 1.0 / veclen;
var dest = [];
dest[0] = v[0] * s;
dest[1] = v[1] * s;
dest[2] = v[2] * s;
return dest;
}
function CalculateSurfaceNormal(TriangleVertexP1, TriangleVertexP2, TriangleVertexP3)
{
var U = [(TriangleVertexP2.x - TriangleVertexP1,x),(TriangleVertexP2.y - TriangleVertexP1.y),(TriangleVertexP2.z - TriangleVertexP1.z)];
var V = [(TriangleVertexP3.x - TriangleVertexP1.x),(TriangleVertexP3.y - TriangleVertexP1.y),(TriangleVertexP3.z - TriangleVertexP1.z)];
var Normal = [];
Normal.x = (U.y * V.z) - (U.z * V.y);
Normal.y = (U.z * V.x) - (U.x * V.z);
Normal.z = (U.x * V.y) - (U.y * V.x);
return Normal;
}
function CalculateSurfaceNormalPolygon(Polygon)
{
var Normal = [0, 0, 0];
var Current = [];
var Next = [];
for (Index=0; Index < Polygon.length; Index++)
{
Current = Polygon.verts[Index];
Next = Polygon.verts[(Index + 1) % Polygon.length];
Normal.x = Normal.x + ((Current.y + Next.y) * (Current.z + Next.z));
Normal.y = Normal.y + ((Current.z + Next.z) * (Current.x + Next.x));
Normal.z = Normal.z + ((Current.x + Next.x) * (Current.y + Next.y));
}
return Normalize(Normal);
}
function CalculateSurfaceNormalNonX(TriangleVertexP1, TriangleVertexP2, TriangleVertexP3)
{
var U = [(TriangleVertexP2[0] - TriangleVertexP1[0]),(TriangleVertexP2[1] - TriangleVertexP1[1]),(TriangleVertexP2[2] - TriangleVertexP1[2])];
var V = [(TriangleVertexP3[0] - TriangleVertexP1[0]),(TriangleVertexP3[1] - TriangleVertexP1[1]),(TriangleVertexP3[2] - TriangleVertexP1[2])];
var Normal = [];
Normal[0] = (U[1] * V[2]) - (U[2] * V[1]);
Normal[1] = (U[2] * V[0]) - (U[0] * V[2]);
Normal[2] = (U[0] * V[1]) - (U[1] * V[0]);
return Normal;
}
function CalculateSurfaceNormalPolygonNonX(Polygon)
{
var Normal = [0, 0, 0];
var Current = [];
var Next = [];
for (Index=0; Index < Polygon.length; Index++)
{
Current = Polygon.verts[Index];
Next = Polygon.verts[(Index + 1) % Polygon.length];
Normal[0] = Normal[0] + ((Current[1] + Next[1]) * (Current[2] + Next[2]));
Normal[1] = Normal[1] + ((Current[2] + Next[2]) * (Current[0] + Next[0]));
Normal[2] = Normal[2] + ((Current[0] + Next[0]) * (Current[1] + Next[1]));
}
return NormalizeNonX(Normal);
}
Sorry, been snowed under with work - I just realised that there is also a method for computing surface normals in the teapot plugin:
https://github.com/xeolabs/scenejs/blob/V3.0/src/plugins/geometry/teapot.js#L5776
I'm thinking of using one of these techniques to the geometry node, so it can automatically calculate normals, maybe to use like this:
node.addNode({
type: "geometry",
positions: ...,
indices: ....,
autoNormals: true
});
I'll play around with it tomorrow.
I've built auto vertex normal calculation into the geometry node -
Live example: http://scenejs.org/examples.html?page=autoNormals
Gist showing the API usage: https://gist.github.com/xeolabs/6142368#file-scenejs-auto-normals-js
I am testing the custom node, and trying to use an image as heightmap and generate the points for a terrain node and then add it to the scene. I am able to load the image, generate the points, however, there is no method for me to load the points into a node only AFTER the image has been loaded and points generated. If the code to addNode is called from the html file from within a getNode function, the data is null for the terrain because it has not been loaded yet. If the terrain data is generated from within an onload function for the image, then the image onload has no method of setting the point data for a scene node. Any suggestions on how to achieve this?