diff options
Diffstat (limited to 'Stars45/Polygon.cpp')
-rw-r--r-- | Stars45/Polygon.cpp | 764 |
1 files changed, 764 insertions, 0 deletions
diff --git a/Stars45/Polygon.cpp b/Stars45/Polygon.cpp new file mode 100644 index 0000000..3eefe87 --- /dev/null +++ b/Stars45/Polygon.cpp @@ -0,0 +1,764 @@ +/* Starshatter OpenSource Distribution + Copyright (c) 1997-2004, Destroyer Studios LLC. + All Rights Reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name "Destroyer Studios" nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + SUBSYSTEM: nGenEx.lib + FILE: Polygon.cpp + AUTHOR: John DiCamillo + + + OVERVIEW + ======== + Polygon and VertexSet structures for 3D rendering +*/ + +#include "MemDebug.h" +#include "Polygon.h" +#include "Bitmap.h" + +// +--------------------------------------------------------------------+ + +VertexSet::VertexSet(int m) + : nverts(0), space(OBJECT_SPACE), tu1(0), tv1(0), tangent(0), binormal(0) +{ + Resize(m); +} + +// +--------------------------------------------------------------------+ + +VertexSet::~VertexSet() +{ + Delete(); +} + +// +--------------------------------------------------------------------+ + +void +VertexSet::Resize(int m, bool preserve) +{ + // easy cases (no data will be preserved): + if (!m || !nverts || !preserve) { + bool additional_tex_coords = (tu1 != 0); + + Delete(); + + nverts = m; + + if (nverts <= 0) { + ZeroMemory(this, sizeof(VertexSet)); + } + + else { + loc = new(__FILE__,__LINE__) Vec3[nverts]; + nrm = new(__FILE__,__LINE__) Vec3[nverts]; + s_loc = new(__FILE__,__LINE__) Vec3[nverts]; + tu = new(__FILE__,__LINE__) float[nverts]; + tv = new(__FILE__,__LINE__) float[nverts]; + rw = new(__FILE__,__LINE__) float[nverts]; + diffuse = new(__FILE__,__LINE__) DWORD[nverts]; + specular = new(__FILE__,__LINE__) DWORD[nverts]; + + if (additional_tex_coords) + CreateAdditionalTexCoords(); + + if (!loc || !nrm || !s_loc || !rw || !tu || !tv || !diffuse || !specular) { + nverts = 0; + + delete [] loc; + delete [] nrm; + delete [] s_loc; + delete [] rw; + delete [] tu; + delete [] tv; + delete [] tu1; + delete [] tv1; + delete [] diffuse; + delete [] specular; + + ZeroMemory(this, sizeof(VertexSet)); + } + } + } + + // actually need to copy data: + else { + int np = nverts; + + nverts = m; + + if (nverts < np) + np = nverts; + + Vec3* new_loc = new(__FILE__,__LINE__) Vec3[nverts]; + Vec3* new_nrm = new(__FILE__,__LINE__) Vec3[nverts]; + Vec3* new_s_loc = new(__FILE__,__LINE__) Vec3[nverts]; + float* new_rw = new(__FILE__,__LINE__) float[nverts]; + float* new_tu = new(__FILE__,__LINE__) float[nverts]; + float* new_tv = new(__FILE__,__LINE__) float[nverts]; + float* new_tu1 = 0; + float* new_tv1 = 0; + DWORD* new_diffuse = new(__FILE__,__LINE__) DWORD[nverts]; + DWORD* new_specular = new(__FILE__,__LINE__) DWORD[nverts]; + + if (tu1) + new_tu1 = new(__FILE__,__LINE__) float[nverts]; + + if (tv1) + new_tv1 = new(__FILE__,__LINE__) float[nverts]; + + if (new_loc) { + CopyMemory(new_loc, loc, np * sizeof(Vec3)); + delete [] loc; + loc = new_loc; + } + + if (new_nrm) { + CopyMemory(new_nrm, nrm, np * sizeof(Vec3)); + delete [] nrm; + nrm = new_nrm; + } + + if (new_s_loc) { + CopyMemory(new_s_loc, s_loc, np * sizeof(Vec3)); + delete [] s_loc; + s_loc = new_s_loc; + } + + if (new_tu) { + CopyMemory(new_tu, tu, np * sizeof(float)); + delete [] tu; + tu = new_tu; + } + + if (new_tv) { + CopyMemory(new_tv, tv, np * sizeof(float)); + delete [] tv; + tv = new_tv; + } + + if (new_tu1) { + CopyMemory(new_tu1, tu1, np * sizeof(float)); + delete [] tu1; + tu = new_tu1; + } + + if (new_tv1) { + CopyMemory(new_tv1, tv1, np * sizeof(float)); + delete [] tv1; + tv = new_tv1; + } + + if (new_diffuse) { + CopyMemory(new_diffuse, diffuse, np * sizeof(DWORD)); + delete [] diffuse; + diffuse = new_diffuse; + } + + if (new_specular) { + CopyMemory(new_specular, specular, np * sizeof(DWORD)); + delete [] specular; + specular = new_specular; + } + + if (!loc || !nrm || !s_loc || !rw || !tu || !tv || !diffuse || !specular) { + Delete(); + ZeroMemory(this, sizeof(VertexSet)); + } + } +} + +// +--------------------------------------------------------------------+ + +void +VertexSet::Delete() +{ + if (nverts) { + delete [] loc; + delete [] nrm; + delete [] s_loc; + delete [] rw; + delete [] tu; + delete [] tv; + delete [] tu1; + delete [] tv1; + delete [] diffuse; + delete [] specular; + delete [] tangent; + delete [] binormal; + + tangent = 0; + binormal = 0; + } +} + +// +--------------------------------------------------------------------+ + +void +VertexSet::Clear() +{ + if (nverts) { + ZeroMemory(loc, sizeof(Vec3) * nverts); + ZeroMemory(nrm, sizeof(Vec3) * nverts); + ZeroMemory(s_loc, sizeof(Vec3) * nverts); + ZeroMemory(tu, sizeof(float) * nverts); + ZeroMemory(tv, sizeof(float) * nverts); + ZeroMemory(rw, sizeof(float) * nverts); + ZeroMemory(diffuse, sizeof(DWORD) * nverts); + ZeroMemory(specular, sizeof(DWORD) * nverts); + + if (tu1) + ZeroMemory(tu1, sizeof(float) * nverts); + + if (tv1) + ZeroMemory(tv1, sizeof(float) * nverts); + + if (tangent) + ZeroMemory(tangent, sizeof(Vec3) * nverts); + + if (binormal) + ZeroMemory(binormal, sizeof(Vec3) * nverts); + } +} + +// +--------------------------------------------------------------------+ + +void +VertexSet::CreateTangents() +{ + if (tangent) delete [] tangent; + if (binormal) delete [] binormal; + + tangent = 0; + binormal = 0; + + if (nverts) { + tangent = new(__FILE__,__LINE__) Vec3[nverts]; + binormal = new(__FILE__,__LINE__) Vec3[nverts]; + } +} + +// +--------------------------------------------------------------------+ + +void +VertexSet::CreateAdditionalTexCoords() +{ + if (tu1) delete [] tu1; + if (tv1) delete [] tv1; + + tu1 = 0; + tv1 = 0; + + if (nverts) { + tu1 = new(__FILE__,__LINE__) float[nverts]; + tv1 = new(__FILE__,__LINE__) float[nverts]; + } +} + +// +--------------------------------------------------------------------+ + +bool +VertexSet::CopyVertex(int dst, int src) +{ + if (src >= 0 && src < nverts && dst >= 0 && dst < nverts) { + loc[dst] = loc[src]; + nrm[dst] = nrm[src]; + s_loc[dst] = s_loc[src]; + tu[dst] = tu[src]; + tv[dst] = tv[src]; + diffuse[dst] = diffuse[src]; + specular[dst] = specular[src]; + + if (tu1) + tu1[dst] = tu1[src]; + + if (tv1) + tv1[dst] = tv1[src]; + + if (tangent) + tangent[dst] = tangent[src]; + + if (binormal) + binormal[dst] = binormal[src]; + + return true; + } + + return false; +} + +VertexSet* +VertexSet::Clone() const +{ + VertexSet* result = new(__FILE__,__LINE__) VertexSet(nverts); + + CopyMemory(result->loc, loc, nverts * sizeof(Vec3)); + CopyMemory(result->nrm, nrm, nverts * sizeof(Vec3)); + CopyMemory(result->s_loc, s_loc, nverts * sizeof(Vec3)); + CopyMemory(result->rw, rw, nverts * sizeof(float)); + CopyMemory(result->tu, tu, nverts * sizeof(float)); + CopyMemory(result->tv, tv, nverts * sizeof(float)); + CopyMemory(result->diffuse, diffuse, nverts * sizeof(DWORD)); + CopyMemory(result->specular, specular, nverts * sizeof(DWORD)); + + if (tu1) { + if (!result->tu1) + result->tu1 = new(__FILE__,__LINE__) float[nverts]; + + CopyMemory(result->tu1, tu1, nverts * sizeof(float)); + } + + if (tv1) { + if (!result->tv1) + result->tv1 = new(__FILE__,__LINE__) float[nverts]; + + CopyMemory(result->tv1, tv1, nverts * sizeof(float)); + } + + if (tangent) { + if (!result->tangent) + result->tangent = new(__FILE__,__LINE__) Vec3[nverts]; + + CopyMemory(result->tangent, tangent, nverts * sizeof(Vec3)); + } + + if (binormal) { + if (!result->binormal) + result->binormal = new(__FILE__,__LINE__) Vec3[nverts]; + + CopyMemory(result->binormal, binormal, nverts * sizeof(Vec3)); + } + + return result; +} + +void +VertexSet::CalcExtents(Point& plus, Point& minus) +{ + plus = Point(-1e6, -1e6, -1e6); + minus = Point( 1e6, 1e6, 1e6); + + for (int i = 0; i < nverts; i++) { + if (loc[i].x > plus.x) plus.x = loc[i].x; + if (loc[i].x < minus.x) minus.x = loc[i].x; + if (loc[i].y > plus.y) plus.y = loc[i].y; + if (loc[i].y < minus.y) minus.y = loc[i].y; + if (loc[i].z > plus.z) plus.z = loc[i].z; + if (loc[i].z < minus.z) minus.z = loc[i].z; + } +} + +// +--------------------------------------------------------------------+ +// +--------------------------------------------------------------------+ +// +--------------------------------------------------------------------+ + +Poly::Poly(int init) + : nverts(0), visible(1), material(0), vertex_set(0), sortval(0), flatness(0) +{ } + +// +--------------------------------------------------------------------+ +// Check to see if a test point is within the bounds of the poly. +// The point is assumed to be coplanar with the poly. Return 1 if +// the point is inside, 0 if the point is outside. + +Vec2 projverts[Poly::MAX_VERTS]; + +static inline double extent3(double a, double b, double c) +{ + double d1 = fabs(a-b); + double d2 = fabs(a-c); + double d3 = fabs(b-c); + + if (d1 > d2) { + if (d1 > d3) + return d1; + else + return d3; + } + else { + if (d2 > d3) + return d2; + else + return d3; + } +} + +int Poly::Contains(const Vec3& pt) const +{ + // find largest 2d projection of this 3d Poly: + int projaxis; + + double pnx = fabs(plane.normal.x); + double pny = fabs(plane.normal.y); + double pnz = fabs(plane.normal.z); + + if (pnx > pny) + if (pnx > pnz) + if (plane.normal.x > 0) + projaxis = 1; + else + projaxis = -1; + else + if (plane.normal.z > 0) + projaxis = 3; + else + projaxis = -3; + else + if (pny > pnz) + if (plane.normal.y > 0) + projaxis = 2; + else + projaxis = -2; + else + if (plane.normal.z > 0) + projaxis = 3; + else + projaxis = -3; + + int i; + + for (i = 0; i < nverts; i++) { + Vec3 loc = vertex_set->loc[verts[i]]; + switch (projaxis) { + case 1: projverts[i] = Vec2(loc.y, loc.z); break; + case -1: projverts[i] = Vec2(loc.z, loc.y); break; + case 2: projverts[i] = Vec2(loc.z, loc.x); break; + case -2: projverts[i] = Vec2(loc.x, loc.z); break; + case 3: projverts[i] = Vec2(loc.x, loc.y); break; + case -3: projverts[i] = Vec2(loc.y, loc.x); break; + } + } + + // now project the test point into the same plane: + Vec2 test; + switch (projaxis) { + case 1: test.x = pt.y; test.y = pt.z; break; + case -1: test.x = pt.z; test.y = pt.y; break; + case 2: test.x = pt.z; test.y = pt.x; break; + case -2: test.x = pt.x; test.y = pt.z; break; + case 3: test.x = pt.x; test.y = pt.y; break; + case -3: test.x = pt.y; test.y = pt.x; break; + } + + const float INSIDE_EPSILON = -0.01f; + + // if the test point is outside of any segment, + // it is outside the entire convex Poly. + for (i = 0; i < nverts-1; i++) { + if (verts[i] != verts[i+1]) { + Vec2 segment = projverts[i+1] - projverts[i]; + Vec2 segnorm = segment.normal(); + Vec2 tdelta = projverts[i] - test; + float inside = segnorm * tdelta; + if (inside < INSIDE_EPSILON) + return 0; + } + } + + // check last segment, too: + if (verts[0] != verts[nverts-1]) { + Vec2 segment = projverts[0] - projverts[nverts-1]; + float inside = segment.normal() * (projverts[0] - test); + if (inside < INSIDE_EPSILON) + return 0; + } + + // still here? must be inside: + return 1; +} + +// +--------------------------------------------------------------------+ +// +--------------------------------------------------------------------+ +// +--------------------------------------------------------------------+ + +Material::Material() + : power(1.0f), brilliance(1.0f), bump(0.0f), blend(MTL_SOLID), + shadow(true), luminous(false), + tex_diffuse(0), tex_specular(0), tex_bumpmap(0), tex_emissive(0), + tex_alternate(0), tex_detail(0), thumbnail(0) +{ + ZeroMemory(name, sizeof(name)); + ZeroMemory(shader, sizeof(shader)); + + ambient_value = 0.2f; + diffuse_value = 1.0f; + specular_value = 0.0f; + emissive_value = 0.0f; +} + +// +--------------------------------------------------------------------+ + +Material::~Material() +{ + // these objects are owned by the shared + // bitmap cache, so don't delete them now: + tex_diffuse = 0; + tex_specular = 0; + tex_bumpmap = 0; + tex_emissive = 0; + tex_alternate = 0; + tex_detail = 0; + + // the thumbnail is unique to the material, + // so it is never cached: + if (thumbnail) + delete thumbnail; +} + +// +--------------------------------------------------------------------+ + +int +Material::operator == (const Material& m) const +{ + if (this == &m) return 1; + + if (Ka != m.Ka) return 0; + if (Kd != m.Kd) return 0; + if (Ks != m.Ks) return 0; + if (Ke != m.Ke) return 0; + if (power != m.power) return 0; + if (brilliance != m.brilliance) return 0; + if (bump != m.bump) return 0; + if (blend != m.blend) return 0; + if (shadow != m.shadow) return 0; + if (tex_diffuse != m.tex_diffuse) return 0; + if (tex_specular != m.tex_specular) return 0; + if (tex_bumpmap != m.tex_bumpmap) return 0; + if (tex_emissive != m.tex_emissive) return 0; + if (tex_alternate != m.tex_alternate) return 0; + if (tex_detail != m.tex_detail) return 0; + + return !strcmp(name, m.name); +} + +// +--------------------------------------------------------------------+ + +void +Material::Clear() +{ + Ka = ColorValue(); + Kd = ColorValue(); + Ks = ColorValue(); + Ke = ColorValue(); + + power = 1.0f; + bump = 0.0f; + blend = MTL_SOLID; + shadow = true; + + tex_diffuse = 0; + tex_specular = 0; + tex_bumpmap = 0; + tex_emissive = 0; + tex_alternate = 0; + tex_detail = 0; +} + +// +--------------------------------------------------------------------+ + +static char shader_name[Material::NAMELEN]; + +const char* +Material::GetShader(int pass) const +{ + int level = 0; + if (pass > 1) pass--; + + for (int i = 0; i < NAMELEN; i++) { + if (shader[i] == '/') { + level++; + + if (level > pass) + return 0; + } + + else if (shader[i] != 0) { + if (level == pass) { + ZeroMemory(shader_name, NAMELEN); + + char* s = shader_name; + while (i < NAMELEN && shader[i] != 0 && shader[i] != '/') { + *s++ = shader[i++]; + } + + return shader_name; + } + } + + else { + return 0; + } + } + + return 0; +} + +// +--------------------------------------------------------------------+ + +void +Material::CreateThumbnail(int size) +{ + if (!thumbnail) { + thumbnail = new(__FILE__,__LINE__) Bitmap(size, size); + } + + if (!thumbnail || thumbnail->Width() != thumbnail->Height()) + return; + + size = thumbnail->Width(); + + DWORD* image = new(__FILE__,__LINE__) DWORD[size*size]; + DWORD* dst = image; + + for (int j = 0; j < size; j++) { + for (int i = 0; i < size; i++) { + *dst++ = GetThumbColor(i, j, size); + } + } + + thumbnail->CopyHighColorImage(size, size, image, Bitmap::BMP_SOLID); +} + +DWORD +Material::GetThumbColor(int i, int j, int size) +{ + Color result = Color::LightGray; + + double x = i - size/2; + double y = j - size/2; + double r = 0.9 * size/2; + double d = sqrt(x*x + y*y); + + if (d <= r) { + double z = sqrt(r*r - x*x - y*y); + + Point loc(x,y,z); + Point nrm = loc; nrm.Normalize(); + Point light(1,-1,1); light.Normalize(); + Point eye(0,0,1); + + ColorValue c = Ka * ColorValue(0.25f, 0.25f, 0.25f); // ambient light + ColorValue white(1,1,1); + + double diffuse = nrm*light; + double v = 1 - (acos(nrm.y)/PI); + double u = asin(nrm.x / sin(acos(nrm.y))) / PI + 0.5; + + ColorValue cd = Kd; + ColorValue cs = Ks; + ColorValue ce = Ke; + + if (tex_diffuse) { + int tu = (int) (u * tex_diffuse->Width()); + int tv = (int) (v * tex_diffuse->Height()); + cd = Kd * tex_diffuse->GetColor(tu,tv); + } + + if (tex_emissive) { + int tu = (int) (u * tex_emissive->Width()); + int tv = (int) (v * tex_emissive->Height()); + ce = Ke * tex_emissive->GetColor(tu,tv); + } + + if (tex_bumpmap && bump != 0 && nrm.z > 0) { + // compute derivatives B(u,v) + int tu = (int) (u * tex_bumpmap->Width()); + int tv = (int) (v * tex_bumpmap->Height()); + + DWORD tmpred = tex_bumpmap->GetColor(tu,tv).Red(); + double du1 = tmpred - tex_bumpmap->GetColor(tu-1,tv).Red(); + double du2 = tex_bumpmap->GetColor(tu+1,tv).Red() - tmpred; + + double dv1 = tmpred - tex_bumpmap->GetColor(tu,tv-1).Red(); + double dv2 = tex_bumpmap->GetColor(tu,tv+1).Red() - tmpred; + + double du = (du1 + du2) / 512 * 1e-8; + double dv = (dv1 + dv2) / 512 * 1e-8; + + if (du || dv) { + Point Nu = nrm.cross(Point(0,-1,0)); Nu.Normalize(); + Point Nv = nrm.cross(Point(1, 0,0)); Nv.Normalize(); + + nrm += (Nu*du*bump); + nrm += (Nv*dv*bump); + nrm.Normalize(); + + diffuse = nrm*light; + v = 1 - (acos(nrm.y)/PI); + u = asin(nrm.x / sin(acos(nrm.y))) / PI + 0.5; + } + } + + if (tex_specular) { + int tu = (int) (u * tex_specular->Width()); + int tv = (int) (v * tex_specular->Height()); + cs = Ks * tex_specular->GetColor(tu,tv); + } + + // anisotropic diffuse lighting + if (brilliance >= 0) { + diffuse = pow(diffuse, (double)brilliance); + } + + // forward lighting + if (diffuse > 0) { + // diffuse + c += cd * (white * diffuse); + + // specular + if (power > 0) { + double spec = ((nrm * 2*(nrm*light) - light) * eye); + if (spec > 0.01) { + spec = pow(spec, (double)power); + c += cs * (white * spec); + } + } + } + + // back lighting + else { + diffuse *= -0.5; + c += cd * (white * diffuse); + + // specular + if (power > 0) { + light *= -1; + + double spec = ((nrm * 2*(nrm*light) - light) * eye); + if (spec > 0.01) { + spec = pow(spec, (double)power); + c += cs * (white * spec) * 0.7; + } + } + } + + c += ce; + + result = c.ToColor(); + } + + return result.Value(); +} |