home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Collection of Hack-Phreak Scene Programs
/
cleanhpvac.zip
/
cleanhpvac
/
CIS_GAME.ARJ
/
QVOXL2.THD
< prev
next >
Wrap
Text File
|
1993-06-25
|
43KB
|
896 lines
______________________________ Subj: Oct Trees ______________________________
Fm: Mark Betz/Ass't SysOp 76605,2346
To: Dan Corritore 70243,1110 (X)
>> I remember that discussion, although I don't recall
anything about using trees in it, but then again,
[ Editor's note: see VOXEL.THD, GAMERS section 11 ]
That subject, which Chris Lampton, at least, knows a lot more about then I,
involves using an Octree (a tree where each node has eight links). I believe
it's done this way (Chris, jump in and tell me far off I am <g>):
You consider your world space as a cube (or in your case a 2D rectangle)
which is subdivided into eight interior cubes, each of which has a link
pointer in the parent cube (in the case of the children of the root node,
their parent is the world space. Within each node, a child pointer is null
(the cube of space is undefined) if there is nothing in it, or has a pointer
to a node if there is. That node is then subdivided according to the same
rule. Each of the cubes is known as a Voxel. The advantage is that you do not
store positional data for cubes (or rectangles) of empty space. As I remember
this is most applicable to flight simulators, where huge portions of the
world database contain nothing at all. In your case I could see using a
quadtree (as above, with a four-level subdivision of a 2D rectangle). Each
node would contain the rectangle extents, and a linked list of objects
present in that rectangle.
--Mark
...........................................................................
Fm: Dan Corritore 70243,1110
To: Mark Betz/Ass't SysOp 76605,2346 (X)
What you said sounds almost exactly like what Kevin Gliner suggested. In
fact, it sounds a whole lot like tiles, although a tile wouldn't have a
linked list, and it wouldn't be on a tree. Which is one thing I can't
understand with what you said.. why not just have an array (the size of the
smallest subdivision you have*(size of the world/subdivision) or something as
such), with linked list pointers coming off of them? That way, you wouldn't
have to traverse the tree to get to a certain section, only to traverse a
linked list afterwards. What I'm using for my tree stuff isn't like
that.. well, actually, I do have a few subdivisions of the world (if it's
32000*32000 pixels, the first node would be at 16000*16000, etc.. until a
suitable enough division is reached.. of course, I could just use an array of
tree's starting at those divisions), but I put the objects into their correct
places(not on linked lists--each object has child pointers which are
traversed based on compares). I hope you understood that..(and I hope
I understood you correctly<g>)
Thanks,
_Dan
...........................................................................
Fm: Mark Betz/Ass't SysOp 76605,2346
To: Dan Corritore 70243,1110
Well, the biggest reason is that, with an array, you'd have to check every
element to see if the linked list was empty or not. With the Octree, if the
space defined by the child pointer is empty, then the pointer is null.
--Mark
...........................................................................
Fm: Hans Peter Rushworth 100031,473
To: Dan Corritore/MOTM 70243,1110
An array would be preferred if you were interested in all points in the 3D
space, even parts that are not visible. In most cases you are interested only
where a surface lies in the the 3D space.
You could store such a surface in a pair of 2D arrays where one array is used
to indicate the altitude of a point in the surface, and the other array is
used to store say a colour value for that point. The first problem with this
if you have a vertical feature like a cliff, you store less surface detail
than where the surface is a plain (which is usually less "interesting"). The
second problem is that even if there are large areas of no interest, the
storage still needs to be allocated for that area.
With a voxel approach, memory is only used where detail is present, and where
the surface exists. Since the voxel structure is fractal, you can add extra
detail to cater for special situations that have a lot of detail, and the
problem of "cliffs" is much less of a problem.
For very large 3D spaces, octree (voxel) storage schemes are much more
efficient than arrays, and they have the added benefit of "adaptive"
resolution control, wherein you restrict the depth of your search for more
distant objects.
In practice you might use a hybrid approach, say have an octree of arrays.
The arrays are there to provide detail that is easier to process (as you say)
You might call this array a voxary <g>
Peter.
...........................................................................
Fm: Hans Peter Rushworth 100031,473
To: John W. Ratcliff 70253,3237 (X)
No John, I have no connection with the commanche people. I'm led to believe
they did not use the voxel surface representation (as I described it) because
in CMO the areas with large change in altitude give rise to "tall rectangles"
which is the result you would get when using two 2D arrays for colour and
altittude. (Unless I remember incorrectly).
Of course it's still possible they used a quadtree in place of either of the
2D arrays, but this would not be computationally efficient IMO.
I have looked at CONTOUR.ZIP and I would agree that CMO probably uses a
similar minmax method to mask displayed pixels in the vertical, but there is
still the problem of ordering the surface altitudes, and I'm not sure what
method you used for that, or whether CMO uses a similar or novel approach.
I have implemented a high speed display system using a different sort of tree
(a Binary Space Partitioning Tree), which was mainly used for hidden surface
removal of polygon data. The BSP tree when appropriately traversed visits
nodes in order of distance, irrespective of the view position. The BSP system
IMO is a very efficient for static, non-intersecting databases.
As for CMO, I suspect they simply calculate the vanishing points of the
horizon where it meets the screen edges (which it is contrived to do always),
and together with the mappings of the bottom edge screen co-ordinates, find a
4-point mapping into their altitude and colour arrays. It is then a simple
matter to use incremental interpolation to sample a vertical "slice"
representing colour and altitudes for each display pixel, and then paint them
using the minmax hidden surface method. I have used a similar sampling scheme
to map flat texture maps into arbitrarily oriented 3D polygons, and using
incremental sampling, the speed can be very fast, even in C.
The sampling scheme automatically deals with the replication and skipping of
map pixels (if the map is visually very close, the same point is sampled more
than once, if distant, pixels are "missed out"). Rotations are dealt with by
transforming only the initial 4 screen corner points. All interpolated
samples that are taken using these points are implicitly rotated.
I could probably knock up a program to demonstrate this using a fractal
plasma surface if you are interested, but you might have to wait a week or
two.
Peter.
...........................................................................
Fm: Mark Betz/Ass't SysOp 76605,2346
To: Hans Peter Rushworth 100031,473 (X)
>> voxarray
I love it! Patent that fast <g>.
I'm no expert on Octrees, but I think I have the basic concept down. What I'm
wondering is what information you consider it necessary to store in each node
of the tree. I'm thinking that, since the node represents a cube, you have to
define the cube's extent. You then have to have a way to travel to the
structures defining the objects in the space. This might be a pointer to a
linked list of objects.
The most puzzling question (at this moment): since the voxel space is
recursively divisible down to a cube of 1 unit on a side, at what point do
you stop and say "this is the cube the object or feature exists in". This is
related to your mention of resolution, but I'm confused as to how to apply
it.
--Mark
...........................................................................
Fm: Hans Peter Rushworth 100031,473
To: Mark Betz/Ass't SysOp 76605,2346 (X)
>> I'm thinking that, since the node represents a cube, you have to define
the cube's extent.
I don't think so Mark. Imagine your 3D space was 1Km x 1Km x 1Km. The root
node of the octree is a cube of those dimensions. If you subdivide this node,
a child of this node is a cube 500m x 500m x 500m (ie one eigth the volume).
The extent of the cube is always known by reference to its depth in the
hierarchy. When "expanding" the octree, you stop when the cube dimensions
would be "the same size as a pixel". If you don't subdivide this way, you
have to do extra checks to make sure you don't have overlapping and
inconsistent spaces. There is no limit to the amount you can subdivide the 3D
space - it is fractal.
>> what to store in the node.
You might store a mean value (lets say colour, or material) of whatever is
stored in children of the node. You can use this value when you decide not to
proceed further in processing the tree.
>> The most puzzling question (at this moment): since the voxel space is
recursively divisible down to a cube of 1 unit on a side, at what point do
you stop and say "this is the cube the object or feature exists in". This is
related to your mention of resolution, but I'm confused as to how to apply
it.
Say your 3D space represents oil trapped beneath the surface of the earth.
The resolution limit might be determined by how big a drill bit you have, or
how large an volume of oil is worth drilling a hole for.
It all depends on what you want to use the octree for.
Peter.
...........................................................................
Fm: Mark Betz/Ass't SysOp 76605,2346
To: Hans Peter Rushworth 100031,473 (X)
I think I see, Peter. The extent of the cube is determined by the division of
the parent cube. The actual location of the child space inside the parent
cube is determined by it's position, i.e. top-left, middle-center, etc.
I assume that the reason you put "pixel" in quotes is that the size of the
pixel is not always one screen pixel. You might have a rectangular solid that
is ten screen pixels on a side, and so you don't deal with it in any finer
resolution.
Am I getting close? There is a good discussion of Voxels in Foley, Van Dam,
et al. I'll have to have a look at it tomorrow.
--Mark
...........................................................................
Fm: Hans Peter Rushworth 100031,473
To: Mark Betz/Ass't SysOp 76605,2346 (X)
>> Am I getting close?
Yes. It doesn't really matter about what you use it for, and therefore what
represents what. The important thing is to realise that the organisation or
"shape" of the tree actually tells you something about the space.
Another interesting aspect is that you can directly get the co-ordinates of
a particular voxel by knowing what route you took to get to it. Each node has
upto 8 subtrees. If you number these links 0 to 7, and organise the links so
that the *bits* in the *link number* indicate the 1-bit "co-ordinates" of the
children in XYZ relative to this node where the X Y & Z bits are bits 0,1 and 2
respectively <thats tricky to explain!>, and as you descend the tree with a
max depth "n" you store the bit number in a list, the co-ordinates (xyz) of
current node is:
x = 0; y = 0; z = 0;
for(int i=0;i<depth;i++)
{
int bit = 1<<(n-i);
if(list[i] & 1) x += bit;
if(list[i] & 2) y += bit;
if(list[i] & 4) z += bit;
}
Working the other way, it is easy to see a way of finding the nearest node
to a point in the space given the X,Y and Z co-ordinates.
Again, assuming "n" is the max depth of the tree:
typedef struct vox_node {
struct vox_node *link[8]; // pointers to children
VOX_DATA data; // whatever...
} VOX_NODE;
VOX_NODE *current = root,*tmp;
// unsigned so "n" can be upto 16 ;)
for(unsigned mask = (1<<(n-1)) ; mask ; mask >>= 1)
{
probe = current->link[ ((x & mask) ? 1 : 0) +
((y & mask) ? 2 : 0) +
((z & mask) ? 4 : 0) ];
if(!probe) break;
current = probe;
}
// Now "current" points at nearest voxel
(sorry about all the shifts, this would be much easier in assembler <g>).
...........................................................................
Fm: Bob Provencher/GD SL 71621,2632
To: Hans Peter Rushworth 100031,473 (X)
Hi Peter,
There was an article a few months ago in C Users Journal that concerned
quad-trees and quad-codes. With a quad-code is was possible to represent
any two-dimensional region. I suppose your oct-tree is the same idea, and
if you take it further you could encode an oct-code representing any
3-dimensional object. Have you seen the article I'm talking about?
Bob
__________________________ Subj: RGB to Oct Tree ___________________________
Fm: Bob Provencher/GD SL 71621,2632
To: Mark Betz/Ass't SysOp 76605,2346 (X)
Hi Mark!
I was thinking about the RGB to octal-code problem, and I discovered the
answer, it turned out to be deceptively simple! I'll follow through my
thinking to see if you agree with my conclusions.
If you set up your octants so that they correspond to increases in the R, G,
and B directions such that an increase in the R (or x) direction adds 2^0 to
the octant number, an increase in the G adds 2^1, and B adds 2^2 then you
come up with an octant map that looks something like this:
111 7
101 110 5 6
100 4
011 3
001 010 1 2
000 0
So compute the octant of an rgb value, you simply decide whether it is the
second half of the line which describes the componant you're looking for.
So, for example, assuming a cube 63 on a side, the first octant for the rgb
value 32,32,32 is 1,1,1 or 7. However, 31, 31, 31 is in octant 0,0,0.
Now what is really interesting, is that if you examine the values you get,
it turns out the computing the octant is a very straightforward. At each
step, to see if a value lays within the first or second half of the desired
line, you need only examine the current high bit.
What this means is that you can compute the oct-code down to the desired
6 octants by simply rotating the bit field that represents the RGB value!
For example:
RGB 63, 31, 0 RGB 0, 0, 0 RGB 63, 0, 63 (Purple)
B - 000000 2^2 B - 000000 B - 111111
G - 011111 2^1 G - 000000 G - 000000
R - 111111 2^0 R - 000000 R - 111111
------ ------ ------
Oct 133333 000000 555555
So, it's very easy to compute the oct-code, and as we discussed to find RGB
values in the same region as the desired one, you only need examine those
with the same leading 5 digits. This will give you the seven values to the
right and up from the desired one. If you were to examine the oct-codes
that had the same 4 leading digits, you would then get values, that could be
to the left of the desired one.
If you use the leading 4 digits, worst case would be 64 matches (highly
unlikely in a 256 color pallette). At that point you'd need to do your
3-d distance computation, but, if you think about it, you only need to
examine the lowest two bits! The max distance here would only be 4, so the
max square root you'd need to compute would be 32! These could easily be
stored in a small lookup table.
What do you think?
Bob
...........................................................................
Fm: Dan Corritore/MOTM 70243,1110
To: Hans Peter Rushworth 100031,473 (X)
I'm sorry, but I still don't understand exactly how this tree structure
works, and, from what Mark said, it sounded like it wastes as much (actually,
more) space as an array, since you have the tree divided up into sections,
and each section either has a null pointer or a linked list.. an array would
have the same stuff, in less space, and less accessing time would be
required.. I guess I need a better description of the voxel tree structure..
Thanks, _Dan
...........................................................................
Fm: Dan Corritore/MOTM 70243,1110
To: Mark Betz/Ass't SysOp 76605,2346 (X)
Aaaaahhhhhh!!! Are we going around in circles, or what?!?<g> You said that
the tree would be divided into sections, each with either a null pointer, or
a linked list coming off of it, yes? That's exactly what the array would
be... an array of linked lists.. if Null, there's no list, so go to your next
location.. same with the tree, except it's more complicated, and takes more
time.. From what I've heard from you, it sounds to me that you are wasting
space both ways.. Thanks,
_Dam
...........................................................................
Fm: Mark Betz/Ass't SysOp 76605,2346
To: Bob Provencher/GD SL 71621,2632 (X)
I think I'm beginning to see, Bob, but you lost me on:
>> So compute the octant of an rgb value, you simply decide whether it is the
>> second half of the line which describes the componant you're looking for.
>> So, for example, assuming a cube 63 on a side, the first octant for the
rgb >> value 32,32,32 is 1,1,1 or 7. However, 31, 31, 31 is in octant 0,0,0.
and also I'm having trouble with the part about "rotating the bitfield that
represents the RGB value". It appears that you're letting each set bit
represent either 0, 2, or 4, and then adding them vertically. I don't get the
part about rotation. Must be missing something basic.
This is exciting. I definitely think you might be on to something. Is there a
possibility of a "collision" in this method? In other words, can two
different triplets produce the same octcode?
--Mark
...........................................................................
Fm: Bob Provencher/GD SL 71621,2632
To: Mark Betz/Ass't SysOp 76605,2346 (X)
Hi Mark -
>> So compute the octant of an rgb value, you simply decide whether it is the
>> second half of the line which describes the componant you're looking for
For R,G and B lines of length 64 (0 through 63) if the value is 31, or less
that bit gets a zero, otherwise it gets a one. Your just figuring out which
half of the line the value lies in.
>> So, for example, assuming a cube 63 on a side, the first octant for the
rgb >> value 32,32,32 is 1,1,1 or 7. However, 31, 31, 31 is in octant 0,0,0.
If the total cube is the cube of sides with length 64, then a
"first-octant" is a subcube of the total cube. 32,32,32 lies in octant 7,
or binary 111 while 31,31,31 lies in octant 0, or binary 000.
>> I'm having trouble with the part about "rotating the bitfield that
>> represents the RGB valueif the RGB value is 64,64,64
R = 111111 = 64
G = 111111 = 64
B = 111111 = 64
Then to get the oct-code you just rotate the bitmap 90 degrees to the right
BGR
111 = 7
111 = 7
111 = 7
111 = 7
111 = 7
111 = 7
to get your oct-code.
I've been thinking about it further. All we're really doing here is
allowing you to do a comparison on the R, G and B values, and give
them equal weight. By rotating, we're saying that we want to find values in
the closest cube, not the closest color. You can define exactly how large
your "closest cube" is by how many low oct-codes you ignore in an octcode.
I still think there is a problem with boundary values, but I have another
idea I'll tell you about tomorrow after I work it out. The problem is
basically that if you're looking for the closest match to 32,32,32, and 31,
31, 31 exists in the pallette, this method wouldn't necesarily _ever_ find it
as the closest, because it's sort of an equal to or greater than comparison.
What we can do is also find the oct-codes for the seven combinations of RGB
values that are one less the the RGB values we're looking for, this will
give us those cubes that are to the left of the one we want.
For example for 32, 32, 32 we should really compute a quad code for
(32,32,32) - (0, 0, 0) = ( itself ) = quadcode 700000
(32,32,32) - (0, 0, 1) = (32, 32, 31) = quadcode 344444
- 0 1 0 = 32, 31, 32 = 522222
- 0 1 1 = 32, 31, 31 = 166666
- 1 0 0 = 31, 32, 32 = 611111
- 1 0 1 = 31, 32, 31 = 255555
- 1 1 0 = 31, 31, 32 = 433333
- 1 1 1 = 31, 31, 31 = 077777
The problem is, even when you drop out the last octit(?) none of the ranges
fallout (become the same), but I think this is worst case. If you take a
regular number at random you might find that some of the resulting codes are
the same after dropping the last octal digit.
This still adds up to the worst case 64 compares since now we're only
ignoring the last digit, not the last two, and comparing cubes to the left
as well as right.
I also see a problem with this if the closest match was greater than 2
away, then you'd have to ignore the last two octal digits and search for
matches. No, maybe that would still be ok?
I'll play with it some more.
Bob
...........................................................................
Fm: Mark Betz/Ass't SysOp 76605,2346
To: Bob Provencher/GD SL 71621,2632 (X)
Oh, I see! That bit about the rotation was just too simple a concept for me
to grasp immediately <g>.
So producing the octcode is very straightforward, but I still don't
understand exactly what you're doing to the extent I'd need to to appreciate
the comments on boundary values. I need to look at it some more, but right
now I'm busy writing a set of small programs for the article. Tell you what,
if you can make it work for a palette lookup, I'll work it into the article
as an alternative technique, for which you will get full credit. Speaking of
which, I have to send you the tree code. Basically the test programs have to
be able to call two functions:
void rgb_sort( char* dactable );
int rgb_match( char r, char g, char b, int t );
The first simply does any initialization work. In my case it builds the tree,
and in yours it would calculate the octcodes and build the array. The second
finds and returns the palette index containing the closest match to the
passed color triplet. The 't' parameter is a threshold value that is specific
to my type of search algorithm. It's basically the vector range on each axis
that we spoke of the other night. You can ignore it.
I'l send the tree package along now, so that you have the latest copy of the
source code. It's very small, and hopefully very clear.
--Mark
...........................................................................
Fm: Bob Provencher/GD SL 71621,2632 # 284350
To: Hans Peter Rushworth 100031,473 (X) Date: 23-Jan-93 17:16:43
Hi Peter -
I've just finished reading OCT.DOC. I think there is some promise to the
method, based on the fact that the 64^3 grid is so sparsely populated, that
I wouldn't need to build the index match down to the sixth level in all
cases.
I can check for an entire cube having an exact index match by computing the
eight corners first, then if they are not equal, computing all the values.
I can readily adapt the idea of octcodes to your method, which means I won't
necessarily have the overhead of a tree, or I can implement it as a tree.
As you point out it may take quite a bit of memory, and I'm also wondering
if building the index values for the 64^3 space (given that we won't have to
compute all of these) will still take too long.
It does sound promising, though, I'm going to try it out, thanks!
One thing though, isn't the formula for computing the distance between to
three dimensional co-ordinates the square-root of the some of the squares of
the x, y and z differences, or sqr( (x1-x2)^2 + (y1-y2)^2 + (z1-z2)^2 )?
...........................................................................
Fm: Hans Peter Rushworth 100031,473 # 284412
To: Bob Provencher/GD SL 71621,2632 (X) Date: 23-Jan-93 19:27:22
>> [distance =] sqr( (x1-x2)^2 + (y1-y2)^2 + (z1-z2)^2 )?
That is right, my brain must have flipped.
>> I can check for an entire cube having an exact index match by computing
the eight corners first, then if they are not equal, computing all the values.
No Bob, If they are not equal, you have to bisect the cube into 8 more cubes
and repeat the test for each new cube recursively. You stop doing this when
all the corners are equal. The only time you actualy check all the points is
when the cube you have is 2x2x2, (obviously because in that case the 8 points
are all the points). Otherwise checking all the points would take a very long
time.
>> based on the fact that the 64^3 grid is so sparsely populated.
Well, it is fully populated (every colour has an index), the storage method
just identifies where the value changes from one to another, it is
effectively storing the boundaries of each clump of the same index in a way
that is easy to search.
>> I can readily adapt the idea of octcodes to your method, which means I
won't necessarily have the overhead of a tree, or I can implement it as a
tree.
I would do it as a tree first, just to make sure the scheme is correct.
...........................................................................
Fm: Bob Provencher/GD SL 71621,2632 # 284511
To: Hans Peter Rushworth 100031,473 (X) Date: 23-Jan-93 22:22:08
>> No Bob, If they are not equal, you have to bisect the cube into 8 more
>> cubes and repeat the test for each new cube recursively. You stop doing
>> this when all
Right, I understood this but sort of typed something completely different
<g>.
>> Well, it is fully populated (every colour has an index), the storage
>> method just identifies where the value changes from one to another, it is
>> effectively storing the boundaries of each clump of the same index in a
>> way that is easy to search.
Right, but there will only be 256 different indexes in the tree. In some
cases we may only have to search the tree to three levels (or match the
octcodes by the left three). Worst case would of course be 6 levels, but
that shouldn't happen unless we have some adjacant or very close colors,
right?
>> I would do it as a tree first, just to make sure the scheme is correct.
You're right, it's easier to do it recursively first and worry about
implementation details later.
...........................................................................
Fm: Hans Peter Rushworth 100031,473 # 284548
To: Bob Provencher/GD SL 71621,2632 (X) Date: 23-Jan-93 23:46:33
>> Right, but there will only be 256 different indexes in the tree. In some
cases we may only have to search the tree to three levels (or match the
octcodes by the left three). Worst case would of course be 6 levels, but
that shouldn't happen unless we have some adjacant or very close colors,
right?
Right. It all depends on where the edges happen to be, you can get lucky.
BTW I have no justification for the "corner" algorithm. I thought of that
while I was writing that file. I've a Gut feeling it is correct, I suppose it
is rather like the 4-colour (or was it 3-colour) map problem. If it fails
then there is probably a faster way of searching for the nearest index. The
way this would work is that you only search indexes that are
(1) inside the cube being tested
(2) already lying along the edges of the cube (IOW test all points along each
of the edges of the cube).
Initially the search set is the entire 256 colour index set. At each level of
recursion, you reduce this by eliminating the cases that don't suceed the two
above tests. Typically this might reduce your colour index set from 256 to
256/8, which is a substantial saving. In fact it might be a good idea to use
this method in place of the 8-point test.
To expand on these tests, consider the following (contrived) quadtree
sub-node case:
1 1 1 1 2 2 2 2
1 * * * * * * 2
1 * *(5)* * * 2
1 * * * * * * 2
4 * * *(6)* * 3
4 * * * * * * 3
4 * * * * * * 3
4 4 4 3 3 3 3 3
You would search all the edge points finding the nearest index. In this case
you find indices 1-4. The indices 5 & 6 lie inside the quad. You can now
eliminate all the other indexes for subsequent recursive tests for the
points marked * and this will be much quicker than checking all 256 of the
indices. You should also be able to eliminate all internal indices (5 & 6)
that don't ALSO appear on an edge from ALL other tests.
This should speed the initial setup (tree)mendously.
Peter.
...........................................................................
Fm: Bob Provencher/GD SL 71621,2632 # 284803
To: Hans Peter Rushworth 100031,473 (X) Date: 24-Jan-93 12:12:33
>> BTW I have no justification for the "corner" algorithm.
Yeah, it seems like it has to be right. I couldn't prove it formally, but I
think it must ok.
>> (1) inside the cube being tested
>> (2) already lying along the edges of the cube (IOW test all points along
>> each of the edges of the cube).
>> Initially the search set is the entire 256 colour index set. A each
>> level of...
I think this sort of what I was trying to do before. It failed whenever the
actual closest index fell just outside the cube. It might have to be
extended to one-half the width of the cube.
...........................................................................
Fm: Mark Betz/Ass't SysOp 76605,2346 # 285922
To: Hans Peter Rushworth 100031,473 (X) Date: 26-Jan-93 00:07:09
I did send it to Bob. I think it was the source of his misery and lack of
time spent with his fiance <g>.
My implementation uses a binary space partitioning tree to partition the RGB
cube at each node. The compare key rotates with each level in the tree, such
that red is compared at level 0, green at level 1, blue at level 2, and so
on. The tree requires 2304 bytes for a 256-color palette, and another 1500
bytes or so for the code to build and search the tree. On my 486 it will
produce an accurate match in about .9 ms, depending on the depth of the
search path. I'm sure it could be made faster, since the nature of palette
data produces a pretty terrible looking binary tree. I'm using a staggered
insertion order when building the tree to avoid hitting any gradient ranges,
but I still get a very deep tree for some palettes. I'd like to know if
there's a way to balance a BSP tree. I suspect there isn't, since the
relative position of nodes in the tree is part of the information contained.
Have any ideas on this?
Btw, what's your schedule this weekend? I'd like to give you a call, and need
a good time. Could you PRI your number to me in case I can't find it?
--Mark
...........................................................................
Fm: Hans Peter Rushworth 100031,473 # 286098
To: Mark Betz/Ass't SysOp 76605,2346 (X) Date: 26-Jan-93 11:20:56
>> I'd like to know if there's a way to balance a BSP tree.
I don't know if this helps in thinking about it, but three levels of BSP tree
can be equivalent to a single octree node.
In that case I would imagine the max depth of your tree is 18 (6x3), and that
sounds reasonable to me. I'm quite amazed that your tree uses only 2304
bytes, which is much less than I would have guessed, but I suppose that can
change depending on the colours that are actually in the palette.
>> the relative position of nodes in the tree is part of the information
contained.
One way to find out is to alter the ordering scheme (instead of R, G then B)
try B, G then R. To balance the tree try to pick the partition (R G or B)
that splits the tree with even "weight" on either side at any given level.
This heuristic won't always give the optimum balance, but is perhaps a good
place to start. Usually you will want to ensure that for three levels of the
tree you have selected an R G & B transition at least once. But Imagine also
a palette that has only Red and Green and no blue colours.
...........................................................................
Fm: Mark Betz/Ass't SysOp 76605,2346 # 286162
To: Hans Peter Rushworth 100031,473 Date: 26-Jan-93 13:59:07
Hi, Peter. Interesting. Btw, the size of the tree doesn't change depending on
the colors in the palette. I don't look for duplicates: each triplet gets a
node. Each node consists of a triplet, two child pointers, and the index of
the original palette entry on which the node is based. Since the tree is
actually built in an array, the child pointers are 2-byte indices, and not 4
byte pointers, saving 2 words per node.
I think your estimate of the max depth is pretty close. Part of the
constraint of searching this kind of tree, is that you have to search both
subtrees anytime the current node falls within a "window" on either side of
the compare key. So for certain colors I end up searching 30-40 nodes. A lot
better than searching the whole palette, but there must be room for
improvement.
--Mark
________________________ Subj: VGA Transparency code ________________________
Fm: VOR Technologies Inc 71333,134 # 312076
To: ALL Date: 11-Mar-93 10:23:25
All,
Does anyone know of some existing code or have a good algorthym for doing
blends/mixes/transparencies etc with the VGA Palette. There are a couple of
things though, given that i am using a static 256 color palette (256 colors
were chosen and cant be changed for this program). Does anyone know of an
algorythm for selecting a value for a color that shows through another color
(i.E. i have a red rectanlg
i place a blue one on top that i deem to be 50% trasnparent i should then be
able to choose something in the purple area). I know various graphics program
do it, but am not sure of an algorythm or existing code that might be
available.
Thanks,
Chris Eisnaugle Vor Technologies Inc.
...........................................................................
Fm: Jaimi McEntire 71700,1202 # 312143
To: VOR Technologies Inc 71333,134 (X) Date: 11-Mar-93 12:49:40
Chris,
You could do it the "Cheesy" way... ie: Multiply the transparent color
register (R,G,and B seperately) byt the transparency amount, multiply the
color which you're plotting on by the remainder, add the R from source and
dest, G from source and dst, and B from source and dest. then divide the
results by 100, and that should give you the optimum RGB. Then just search
through your palette to find the closest match.
pixel = GetPixel(x,y)
pixel.red *= transparency_perc;
pixel.green *= transparency_perc;
pixel.blue *= transparency_perc;
destpixel = GetPixel(destx,desty);
destpixel.red *= (100-transparency_perc);
destpixel.green *= (100-transparency_perc);
destpixel.blue *= (100-transparency_perc);
optimum.red = (pixel.red + destpixel.red)/100;
optimum.green = (pixel.green + destpixel.green)/100;
optimum.blue = (pixel.blue + destpixel.blue)/100;
// now find optimum color in palette.
// and set dest pixel to closest to optimum
Good luck!
P.S. this wont compile obviously. It's just an algorithm and a slow one.
you could get much better performance if you used shifts, instead of
divides, but then you would need to limit the transparency levels.
Jaimi McEntire
...........................................................................
Fm: Mark Betz/Ass't SysOp 76605,2346 # 312351
To: VOR Technologies Inc 71333,134 (X) Date: 11-Mar-93 20:36:07
Hi, Chris. If you want to put Jaimi's method to work, I have some code which
will select the best match for a given RGB from a palette. It builds a binary
search tree of color values, and takes about .10 ms to do a lookup on my
486-33. The code and data is about 5K.
--Mark
...........................................................................
Fm: Mark Betz/Ass't SysOp 76605,2346 # 312780
To: VOR Technologies Inc 71333,134 (X) Date: 12-Mar-93 21:26:44
I'll send it along tonight, Chris. The code is public domain, but I'd
appreciate it if you didn't distribute it until after July, since it is part
of an article that I have submitted to a magazine.
--Mark
___________________________ Subj: Mark's article ___________________________
Fm: Patrick Reilly 71333,2764 # 376998
To: Mark Betz/Ass't SysOp 76605,2346 (X) Date: 15-Jun-93 19:49:12
Mark,
Thanks - I look forward to reading it (haven't gotten the last DDJ yet)...
I was thinking about bitmap transforms; do you know if its possible,
probable, desirable, or already done <g> to take a square bitmap whose
elements are made up of a RGB triple and have a transform matrix that, if you
multiply your bitmap times it, will shade it for distance and do a "nice"
rotation/translation of it? IOW if you start out with, say, a 4x4 matrix
that's a bitmap that represents a (tiny) wall in RGB triplets when viewed
head-on at a distance of 0 - can a single matrix of RGBs be created that will
translate, rotate, and shade for distance the bitmap (with a specific RGB
quantity - say, <0,0,0> set to be 'transparent') so that if you now displayed
the bitmap, it would look like a 3D rendered texture of a wall that's 3 units
away and rotated about the vertical axis 60 degrees (with depth shading)?
I was going to start playing with the idea (this would allow having all the
bitmaps for the walls, floors and ceilings loaded into memory, a list of
about 40 or so of these transform matrices, and drawing a 'world' would only
require doing the matrix multiply for each panel with the appropriate
transform, do another transform for the new bitmap's starting <x,y> location
on the screen, and displaying) but if this is 'old hat' <g> or has been tried
and found lacking, or is plain impossible, then I won't waste my time.
I read Abrash's articles for X-Sharp, but for something like a fixed view
of panels which, in real-world coords, form a lattice, it seems that's not a
very good approach...
-Pat-
...........................................................................
Fm: Patrick Reilly 71333,2764 # 377238
To: Mark Betz/Ass't SysOp 76605,2346 (X) Date: 16-Jun-93 00:17:25
Mark,
I figured to use a lookup table to map the RGB triplet to a palette entry;
this would eliminate the problem of having to have the palette laid out so
that, for example, dark colors are near 0, light colors near 255, etc, but
would still allow having a triplet so that the transform matrix would make
'sense'. Also, multiplying the bitmap by a scalar would make sense too: IE
using a scalar of <0.75,0.75,0.75> would darken the whole bitmap evenly to
3/4 intensity. With a lookup table, I would think the conversion (from 1-byte
element matrix to RGB element matrix) would be quick enough. Of course, each
conversion would require a filter to 'round' the RGB value to the 'nearest'
mapping element.
So you haven't heard of anyone trying this? I'll have to play a bit <g>;
what I would like to end up with is: 1) convert the NxN bitmap to an NxN RGB
matrix; 2) multiply the matrix by the appropriate transform matrix (IE if
this panel is the 'floor' at cell <-3,+4> from current POV, use
transformMatrix[18]); 3) use a filter on the transformed matrix so that the
RGB elements can map to palette entries; 4) convert the matrix back into a
bitmap; 5) look up the <x,y> pair for a floor at <-3,+4> from the POV and
paint the bitmap at <x,y>. If it *can* be done (ie one matrix will do the
complete conversion), then a nice, slow, floating-point program can generate
all the transforms and lookup tables for the <x,y> coords. Once these are
calculated, they can just be hardcoded into the program.
I'll let you know if I can come up with anything.
-Pat-
...........................................................................
Fm: Patrick Reilly 71333,2764 # 377289
To: Mark Betz/Ass't SysOp 76605,2346 (X) Date: 16-Jun-93 04:27:26
Mark,
Oh well... its a no-show on a simple transform. A simple example shows why:
consider having a 4x4 bitmap (elements A(ij)), and wanting it to be
transformed onto the 'floor' panel immediately in front of the view plane;
what I'd want when the transform is complete would be for A(41) to have the
colors 0.65*(0.5*A(41) + 0.5*A(31)). To get such a product, the bitmap would
have to multiply on the right the transform matrix; but A(32) wants to be
0.03*A(31) + 0.5*A(32) + ..., which means that the bitmap has to multiply on
the *left* the transform matrix...
I'll play some more; maybe its possible to use 2 matrices; one that
multiplies on the left and transforms/alias' vertical pixels, and one that
multiplies on the right and transforms/alias' horizontal pixels, then sum the
two resultant RGB bitmaps... The bummer is that now I'd have to store *twice*
as many matrices...
...........................................................................