2681 lines
118 KiB
C++
2681 lines
118 KiB
C++
/*
|
|
** Command & Conquer Renegade(tm)
|
|
** Copyright 2025 Electronic Arts Inc.
|
|
**
|
|
** This program is free software: you can redistribute it and/or modify
|
|
** it under the terms of the GNU General Public License as published by
|
|
** the Free Software Foundation, either version 3 of the License, or
|
|
** (at your option) any later version.
|
|
**
|
|
** This program is distributed in the hope that it will be useful,
|
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
** GNU General Public License for more details.
|
|
**
|
|
** You should have received a copy of the GNU General Public License
|
|
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/***********************************************************************************************
|
|
*** Confidential - Westwood Studios ***
|
|
***********************************************************************************************
|
|
* *
|
|
* Project Name : LightMap *
|
|
* *
|
|
* $Archive:: /Commando/Code/Tool $*
|
|
* *
|
|
* $Author:: Ian_l $*
|
|
* *
|
|
* $Modtime:: 8/24/01 9:24p $*
|
|
* *
|
|
* $Revision:: 60 $*
|
|
* *
|
|
*---------------------------------------------------------------------------------------------*
|
|
* Functions: *
|
|
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
|
|
// Includes.
|
|
#include "StdAfx.h"
|
|
#include "LightMap.h"
|
|
#include "Lightscape.h"
|
|
#include "LightMapPacker.h"
|
|
#include "OptionsDialog.h"
|
|
#include "PerlinNoise.h"
|
|
#include "StringBuilder.h"
|
|
#include "TextureNameNode.h"
|
|
#include "matrix3.h"
|
|
#include "matrix3d.h"
|
|
#include "lightexclude.h"
|
|
#include "tri.h"
|
|
#include "vector3.h"
|
|
#include "wwmath.h"
|
|
#include <float.h>
|
|
#include <math.h>
|
|
#include <typeinfo.h>
|
|
|
|
|
|
// Static data.
|
|
LightscapeSolve *LightscapeSolve::_ActiveImporter = NULL;
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LightscapeSolve::LightscapeSolve -- *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 7/27/99 IML : Created. *
|
|
*=============================================================================================*/
|
|
LightscapeSolve::LightscapeSolve (const char *solvedirectoryname, const char *solvefilenamelist, CStatusBar* statusptr, const char *statusbarmessage, bool blendnoise)
|
|
: LightmapPacker()
|
|
{
|
|
const char *notsolutiontext =
|
|
"One or more solve files are not solution (.ls) files.";
|
|
|
|
const int materialtexturenamegrowthstep = 256;
|
|
const int patchfacegrowthstep = 1024;
|
|
const int vertexgrowthstep = 2048;
|
|
const int lightgrowthstep = 32;
|
|
|
|
static char _messagebuffer [1024];
|
|
|
|
CRect srect, trect;
|
|
BOOL success;
|
|
StringBuilder errormessage (_messagebuffer, sizeof (_messagebuffer));
|
|
const char *solvefilename;
|
|
LtTBuilderFactory *factory = NULL;
|
|
OptionsDialog options;
|
|
|
|
// Initialize.
|
|
SmoothingAngle = DEG_TO_RAD (options.Get_Smoothing_Angle()); // Convert from degrees to radians.
|
|
SpatialTolerance = options.Get_Spatial_Tolerance(); // No mapping required.
|
|
FilterSharpness = pow (options.Get_Filter_Sharpness() / 50.0l, 5.0l); // Map from linear to exponential scale.
|
|
|
|
// If noise has been requested create a Perlin Noise generator.
|
|
if (blendnoise) {
|
|
ProceduralTexture = new PerlinNoise;
|
|
ASSERT (ProceduralTexture != NULL);
|
|
} else {
|
|
ProceduralTexture = NULL;
|
|
}
|
|
|
|
if (options.Get_Light_Export_Selective()) {
|
|
LightExclusionString = options.Get_Light_Exclusion_String();
|
|
} else {
|
|
LightExclusionString = NULL;
|
|
}
|
|
|
|
statusptr->GetItemRect (0, &srect);
|
|
statusptr->SetPaneText (0, statusbarmessage);
|
|
statusptr->GetDC()->DrawText (statusbarmessage, -1, &trect, DT_CALCRECT);
|
|
|
|
CRect prect (srect.TopLeft().x + trect.Width(), srect.TopLeft().y, srect.BottomRight().x, srect.BottomRight().y);
|
|
|
|
ProgressBar = new CProgressCtrl;
|
|
ASSERT (ProgressBar != NULL);
|
|
success = ProgressBar->Create (WS_CHILD | WS_VISIBLE | PBS_SMOOTH, prect, statusptr, 0);
|
|
ASSERT (success);
|
|
|
|
// Specify how many elements should be added to dynamic arrays when they are resized.
|
|
MaterialTextureNames.Set_Growth_Step (materialtexturenamegrowthstep);
|
|
PatchFaces.Set_Growth_Step (patchfacegrowthstep);
|
|
Vertices.Set_Growth_Step (vertexgrowthstep);
|
|
VertexIndices.Set_Growth_Step (vertexgrowthstep);
|
|
Lights.Set_Growth_Step (lightgrowthstep);
|
|
|
|
_ActiveImporter = this;
|
|
|
|
try {
|
|
|
|
char solvepathname [_MAX_PATH];
|
|
int totalpatchclustercount = 0;
|
|
|
|
// Parse the files, check that they are solve files and sum the patch cluster count for the progress bar.
|
|
solvefilename = solvefilenamelist;
|
|
while (strlen (solvefilename) > 0) {
|
|
strcpy (solvepathname, solvedirectoryname);
|
|
strcat (solvepathname, solvefilename);
|
|
factory = new LsInformationFactory;
|
|
ASSERT (factory != NULL);
|
|
::LtSolutionImport (solvepathname, *factory);
|
|
if (!IsSolution) throw (notsolutiontext);
|
|
totalpatchclustercount += PatchClusterCount;
|
|
errormessage.Copy (factory->GetErrorMsg());
|
|
delete factory;
|
|
factory = NULL;
|
|
if (strlen (errormessage.String()) > 0) throw (errormessage.String());
|
|
|
|
// Advance to next solve file.
|
|
solvefilename += strlen (solvefilename) + 1;
|
|
}
|
|
|
|
// Set-up the progress bar so that it can be updated for every patch cluster.
|
|
ProgressBar->SetRange32 (0, totalpatchclustercount);
|
|
ProgressBar->SetStep (1);
|
|
|
|
// For each solve filename in the list...
|
|
solvefilename = solvefilenamelist;
|
|
while (strlen (solvefilename) > 0) {
|
|
|
|
strcpy (solvepathname, solvedirectoryname);
|
|
strcat (solvepathname, solvefilename);
|
|
|
|
// Parse the Lightscape solve file and process materials.
|
|
// NOTE: Material processing must be done first because mesh processing is dependant on it.
|
|
// In order to guarantee this, the solution file is parsed twice, so that there is no
|
|
// implicit reliance on the material data preceeding the mesh data in the file.
|
|
factory = new LsPreparationFactory;
|
|
ASSERT (factory != NULL);
|
|
|
|
::LtSolutionImport (solvepathname, *factory);
|
|
errormessage.Copy (factory->GetErrorMsg());
|
|
delete factory;
|
|
factory = NULL;
|
|
if (strlen (errormessage.String()) > 0) throw (errormessage.String());
|
|
|
|
// Sort the material-texture names by a key defined by the comparison function Compare_Material_Names().
|
|
// NOTE: qsort MUST (and does) allow for duplicate keys.
|
|
if (MaterialTextureNames.Count() > 0) {
|
|
qsort (&MaterialTextureNames [0], MaterialTextureNames.Count(), sizeof (char*), Compare_Material_Texture_Names);
|
|
}
|
|
|
|
// Parse the Lightscape solve file and add all vertices and faces that have been
|
|
// processed by Mesh to Texture.
|
|
factory = new LsMainFactory;
|
|
ASSERT (factory != NULL);
|
|
|
|
::LtSolutionImport (solvepathname, *factory);
|
|
errormessage.Copy (factory->GetErrorMsg());
|
|
delete factory;
|
|
factory = NULL;
|
|
if (strlen (errormessage.String()) > 0) throw (errormessage.String());
|
|
|
|
// Clean-up the the material-texture names. They are no longer required.
|
|
for (int i = 0; i < MaterialTextureNames.Count(); i++) {
|
|
delete [] MaterialTextureNames [i];
|
|
}
|
|
MaterialTextureNames.Clear();
|
|
|
|
// Advance to next solve file.
|
|
solvefilename += strlen (solvefilename) + 1;
|
|
}
|
|
|
|
if (Vertices.Count() > 0) {
|
|
|
|
// Create a vertex index table and initialize the indices to identity.
|
|
for (int v = 0; v < Vertices.Count(); v++) {
|
|
VertexIndices.Insert (v, v);
|
|
}
|
|
|
|
// Sort the vertex pointers by a key defined by the comparison function Compare_Vertices().
|
|
// NOTE: qsort MUST (and does) allow for duplicate keys.
|
|
qsort (&VertexIndices [0], VertexIndices.Count(), sizeof (unsigned), Compare_Vertices);
|
|
}
|
|
|
|
// Sort the patch faces by a key defined by the comparison function Compare_Patch_Faces().
|
|
// NOTE: qsort MUST (and does) allow for duplicate keys.
|
|
if (PatchFaces.Count() > 0) {
|
|
qsort (&PatchFaces [0], PatchFaces.Count(), sizeof (PatchFaceStruct), Compare_Patch_Faces);
|
|
}
|
|
|
|
} catch (const char *errormessage) {
|
|
|
|
// Clean-up.
|
|
if (factory != NULL) delete factory;
|
|
if (ProgressBar != NULL) {
|
|
delete ProgressBar;
|
|
ProgressBar = NULL;
|
|
}
|
|
|
|
// Re-throw the message to the caller.
|
|
throw (errormessage);
|
|
}
|
|
|
|
_ActiveImporter = NULL;
|
|
delete ProgressBar;
|
|
ProgressBar = NULL;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LightscapeSolve::Finish -- *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 7/27/99 IML : Created. *
|
|
*=============================================================================================*/
|
|
LightscapeSolve::Finish()
|
|
{
|
|
LightmapPacker::Finish();
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LightscapeSolve::~LightscapeSolve -- *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: Do not throw any exceptions in this (and any other) destructor because this *
|
|
* destructor can itself be called during the exception as part of its 'clean-up'. *
|
|
* *
|
|
* HISTORY: *
|
|
* 7/27/99 IML : Created. *
|
|
*=============================================================================================*/
|
|
LightscapeSolve::~LightscapeSolve()
|
|
{
|
|
int i, l;
|
|
|
|
// Clean-up anything that didn't get cleaned up before because an exception was thrown.
|
|
for (i = 0; i < MaterialTextureNames.Count(); i++) {
|
|
delete [] MaterialTextureNames [i];
|
|
}
|
|
_ActiveImporter = NULL;
|
|
|
|
// Clean-up the texture names.
|
|
for (i = 0; i < TextureNames.Length(); i++) {
|
|
|
|
TextureNameNode *texturenamenode, *removalnode;
|
|
|
|
texturenamenode = TextureNames [i];
|
|
while (texturenamenode != NULL) {
|
|
removalnode = texturenamenode;
|
|
texturenamenode = texturenamenode->Next;
|
|
delete removalnode;
|
|
}
|
|
}
|
|
|
|
// Release references to lights.
|
|
for (l = 0; l < Lights.Count(); l++) {
|
|
Lights [l]->Release_Ref();
|
|
}
|
|
|
|
if (ProceduralTexture != NULL) delete ProceduralTexture;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LightscapeSolve::Add_Material_Texture_Name -- *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 7/27/99 IML : Created. *
|
|
*=============================================================================================*/
|
|
void LightscapeSolve::Add_Material_Texture_Name (const char *materialname, const char *texturepathname)
|
|
{
|
|
const char *invalidtexturepathname = "";
|
|
|
|
unsigned materialnamelength, texturepathnamelength;
|
|
char *name;
|
|
|
|
// The material name must exist.
|
|
ASSERT (materialname != NULL);
|
|
|
|
// If the material has no associated texture use the invalid texture pathname.
|
|
if (texturepathname == NULL) texturepathname = invalidtexturepathname;
|
|
|
|
// Calculate length of strings including terminator.
|
|
materialnamelength = strlen (materialname) + 1;
|
|
texturepathnamelength = strlen (texturepathname) + 1;
|
|
|
|
name = new char [materialnamelength + texturepathnamelength];
|
|
ASSERT (name != NULL);
|
|
|
|
strcpy (name, materialname);
|
|
|
|
// Append the texture name to the material name but leave the material name's terminator
|
|
// intact so that conventional string functions only 'see' the material name.
|
|
strcpy (name + materialnamelength, texturepathname);
|
|
|
|
MaterialTextureNames.Add (name);
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LightscapeSolve::Add_Texture_Name -- *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 9/16/99 IML : Created. *
|
|
*=============================================================================================*/
|
|
void LightscapeSolve::Add_Texture_Name (unsigned patchindex, const char *materialname)
|
|
{
|
|
const char *materialnotfoundtext =
|
|
"There is a face in the model that has a material name that cannot be found.";
|
|
|
|
int length;
|
|
char **materialnameptr;
|
|
char *texturepathname;
|
|
unsigned texturepathnamelength;
|
|
|
|
// Can the texture name data be accomodated by the texture name array?
|
|
length = TextureNames.Length();
|
|
if ((int) patchindex >= length) {
|
|
|
|
const int growthstep = 1024; // Add this many elements if the vector array runs out of room.
|
|
|
|
bool success = TextureNames.Resize (patchindex + growthstep);
|
|
ASSERT (success);
|
|
|
|
// Null out the new elements.
|
|
for (int i = length; i < TextureNames.Length(); i++) {
|
|
TextureNames [i] = NULL;
|
|
}
|
|
}
|
|
|
|
// NOTE: The material-texture names MUST have been sorted prior to calling bsearch().
|
|
if (MaterialTextureNames.Count() > 0) {
|
|
|
|
const char **keyptr;
|
|
const char *key;
|
|
|
|
key = materialname;
|
|
keyptr = &key;
|
|
|
|
materialnameptr = (char**) bsearch (keyptr, &MaterialTextureNames [0], MaterialTextureNames.Count(), sizeof (char*), Compare_Material_Texture_Names);
|
|
} else {
|
|
materialnameptr = NULL;
|
|
}
|
|
|
|
// If the material cannot be found then either the database has malfunctioned or the solve
|
|
// file is corrupt.
|
|
if (materialnameptr == NULL) {
|
|
ASSERT (FALSE);
|
|
throw (materialnotfoundtext);
|
|
}
|
|
|
|
// Extract the texture name.
|
|
texturepathname = *materialnameptr + strlen (*materialnameptr) + 1;
|
|
texturepathnamelength = strlen (texturepathname) + 1;
|
|
|
|
// Add the name to the database only if it exists (has non-zero length excluding terminator).
|
|
if (texturepathnamelength > 1) {
|
|
|
|
TextureNameNode *node;
|
|
|
|
node = new TextureNameNode (texturepathname);
|
|
ASSERT (node != NULL);
|
|
|
|
// Add the texture name to the database.
|
|
node->Next = TextureNames [patchindex];
|
|
|
|
TextureNames [patchindex] = node;
|
|
}
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LightscapeSolve::Add_Light -- *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 3/24/00 IML : Created. *
|
|
*=============================================================================================*/
|
|
void LightscapeSolve::Add_Light (LightClass *light)
|
|
{
|
|
Lights.Add (light);
|
|
light->Add_Ref();
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LightscapeSolve::Add_Vertex -- *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 1/25/00 IML : Created. *
|
|
*=============================================================================================*/
|
|
bool LightscapeSolve::Add_Vertex (unsigned vertexindex, const Vector3 &p, const Vector3 &n, unsigned patchindex, const Vector2 &t)
|
|
{
|
|
bool success;
|
|
|
|
// If adding a M2T vertex, ensure that this is a M2T solve.
|
|
ASSERT (Importer()->Is_M2T_Solve());
|
|
|
|
// Has the element already been inserted?
|
|
if (((int) vertexindex) < Vertices.Count()) {
|
|
|
|
// Check that the new element data matches the existing data.
|
|
success = (Vertices [vertexindex].Point == p) &&
|
|
(Vertices [vertexindex].FaceNormal == n) &&
|
|
(Vertices [vertexindex].PatchIndex == patchindex);
|
|
|
|
} else {
|
|
|
|
VerticesStruct vertex;
|
|
|
|
vertex.Point = p;
|
|
vertex.FaceNormal = n;
|
|
vertex.PatchIndex = patchindex;
|
|
vertex.ValidColor = false;
|
|
vertex.ValidUV = false;
|
|
success = Vertices.Insert (vertexindex, vertex);
|
|
}
|
|
|
|
if (success) {
|
|
Vertices [vertexindex].UV = t;
|
|
Vertices [vertexindex].ValidUV = true;
|
|
}
|
|
|
|
return (success);
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LightscapeSolve::Add_Vertex -- *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 1/25/00 IML : Created. *
|
|
*=============================================================================================*/
|
|
bool LightscapeSolve::Add_Vertex (unsigned vertexindex, const Vector3 &p, const Vector3 &n, unsigned patchindex, const ColorVector &c)
|
|
{
|
|
bool success;
|
|
|
|
// Has the element already been inserted?
|
|
if (((int) vertexindex) < Vertices.Count()) {
|
|
|
|
// Check that the new element data matches the existing data.
|
|
success = (Vertices [vertexindex].Point == p) &&
|
|
(Vertices [vertexindex].FaceNormal == n) &&
|
|
(Vertices [vertexindex].PatchIndex == patchindex);
|
|
|
|
} else {
|
|
|
|
VerticesStruct vertex;
|
|
|
|
vertex.Point = p;
|
|
vertex.FaceNormal = n;
|
|
vertex.PatchIndex = patchindex;
|
|
vertex.ValidColor = false;
|
|
vertex.ValidUV = false;
|
|
success = Vertices.Insert (vertexindex, vertex);
|
|
}
|
|
|
|
if (success) {
|
|
|
|
// Only add/set the color if this is not a M2T solve.
|
|
if (!Importer()->Is_M2T_Solve()) {
|
|
|
|
// Add or set the color?
|
|
if (Vertices [vertexindex].ValidColor) {
|
|
|
|
const ColorVector clamp (1.0f, 1.0f, 1.0f);
|
|
|
|
// Add and clamp.
|
|
Vertices [vertexindex].Color += c;
|
|
Vertices [vertexindex].Color.Update_Min (clamp);
|
|
|
|
} else {
|
|
|
|
Vertices [vertexindex].Color = c;
|
|
Vertices [vertexindex].ValidColor = true;
|
|
}
|
|
}
|
|
}
|
|
return (success);
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LightscapeSolve::Add_Patch_Face -- *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 9/16/99 IML : Created. *
|
|
*=============================================================================================*/
|
|
bool LightscapeSolve::Add_Patch_Face (unsigned patchfaceindex, unsigned patchindex, unsigned vertexindexcount, unsigned *vertexindices)
|
|
{
|
|
bool success;
|
|
|
|
// Only 3 or 4 vertex indices are supported.
|
|
ASSERT (vertexindexcount == 3 || vertexindexcount == 4);
|
|
|
|
// Has the element already been inserted?
|
|
if (((int) patchfaceindex) < PatchFaces.Count()) {
|
|
|
|
// Check that the new element data matches the existing data.
|
|
success = (patchindex == PatchFaces [patchfaceindex].PatchIndex) &&
|
|
(vertexindices [0] == PatchFaces [patchfaceindex].VertexIndices [0]) &&
|
|
(vertexindices [1] == PatchFaces [patchfaceindex].VertexIndices [1]) &&
|
|
(vertexindices [2] == PatchFaces [patchfaceindex].VertexIndices [2]);
|
|
|
|
if (vertexindexcount == 4) {
|
|
success &= (vertexindices [3] == PatchFaces [patchfaceindex].VertexIndices [3]);
|
|
}
|
|
|
|
} else {
|
|
|
|
PatchFaceStruct patchface;
|
|
|
|
patchface.PatchIndex = patchindex;
|
|
patchface.VertexIndices [0] = vertexindices [0];
|
|
patchface.VertexIndices [1] = vertexindices [1];
|
|
patchface.VertexIndices [2] = vertexindices [2];
|
|
if (vertexindexcount == 4) {
|
|
patchface.VertexIndices [3] = vertexindices [3];
|
|
} else {
|
|
patchface.VertexIndices [3] = PatchFaceStruct::VERTEX_INDEX_INVALID;
|
|
}
|
|
success = PatchFaces.Insert (patchfaceindex, patchface);
|
|
}
|
|
|
|
return (success);
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LightscapeSolve::Find_Vertex -- *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 1/07/00 IML : Created. *
|
|
*=============================================================================================*/
|
|
void LightscapeSolve::Find_Vertex (const Vector3 &point, const Vector3 *smoothingnormalptr, W3dRGBStruct &vertexcolor, SolveStatistics &solvestatistics)
|
|
{
|
|
const int growthstep = 256;
|
|
const W3dRGBStruct pink (255, 0, 255);
|
|
|
|
DynamicVectorClass <VerticesStruct*> vertexset;
|
|
ColorVector color;
|
|
unsigned count;
|
|
|
|
vertexset.Set_Growth_Step (growthstep);
|
|
Find_Vertices (point, SpatialTolerance, vertexset);
|
|
if (vertexset.Count() > 0) {
|
|
|
|
color.Set (0.0f, 0.0f, 0.0f);
|
|
count = 0;
|
|
if (smoothingnormalptr != NULL) {
|
|
|
|
// Sum the colors of all vertices that lie within the smoothing normal.
|
|
for (int v = 0; v < vertexset.Count(); v++) {
|
|
|
|
if (vertexset [v]->ValidColor) {
|
|
|
|
float angle;
|
|
|
|
// Is the face that is associated with this vertex within the smoothing angle
|
|
// of the supplied vertex normal?
|
|
angle = acosf (Vector3::Dot_Product (*smoothingnormalptr, vertexset [v]->FaceNormal));
|
|
if (angle <= SmoothingAngle) {
|
|
color += vertexset [v]->Color;
|
|
count++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (count > 0) {
|
|
color /= ((float) count);
|
|
vertexcolor.Set (color.X, color.Y, color.Z);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Sum the colors of all vertices regardless of smoothing normal.
|
|
for (int v = 0; v < vertexset.Count(); v++) {
|
|
if (vertexset [v]->ValidColor) {
|
|
color += vertexset [v]->Color;
|
|
count++;
|
|
}
|
|
}
|
|
|
|
if (count > 0) {
|
|
color /= ((float) count);
|
|
vertexcolor.Set (color.X, color.Y, color.Z);
|
|
solvestatistics.Count [SolveStatistics::VERTEX_NOT_SMOOTH]++;
|
|
return;
|
|
} else {
|
|
|
|
// There are no vertices with a valid color. Return a place-holder color.
|
|
vertexcolor = pink;
|
|
solvestatistics.Count [SolveStatistics::VERTEX_NO_COLOR]++;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Vertex not found. Return a place-holder color.
|
|
vertexcolor = pink;
|
|
solvestatistics.Count [SolveStatistics::VERTEX_NOT_FOUND]++;
|
|
return;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LightscapeSolve::Find_Triangle -- *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 06/07/00 IML : Created. *
|
|
*=============================================================================================*/
|
|
void LightscapeSolve::Find_Triangle (const Vector3 *points, const Vector3 &normal, PackingTriangle &triangle, SolveStatistics &solvestatistics)
|
|
{
|
|
const unsigned notextureid = 0xffffffff; // Arbitrary ID used to indicate that there is no associated texture.
|
|
|
|
VerticesStruct trianglevertices [Triangle::VERTICES_COUNT];
|
|
FaceResultEnum faceresult;
|
|
bool notexture;
|
|
unsigned v;
|
|
|
|
ASSERT (points != NULL);
|
|
|
|
// First, attempt a match with the face itself.
|
|
switch (Face_Size (points, Triangle::VERTICES_COUNT)) {
|
|
|
|
case FACE_UNDERSIZED:
|
|
case FACE_NOT_UNDERSIZED:
|
|
Find_Triangle (points, normal, trianglevertices, faceresult);
|
|
break;
|
|
|
|
case FACE_DEGENERATE:
|
|
solvestatistics.Count [SolveStatistics::FACE_DEGENERATE]++;
|
|
faceresult = FACE_NOT_FOUND;
|
|
break;
|
|
}
|
|
|
|
notexture = false;
|
|
switch (faceresult) {
|
|
|
|
case FACE_AMBIGUOUS:
|
|
solvestatistics.Count [SolveStatistics::FACE_AMBIGUOUS]++;
|
|
// No break.
|
|
|
|
case FACE_FOUND:
|
|
{
|
|
TextureNameNode *texturenamenodeptr;
|
|
bool validuvs;
|
|
|
|
// Check that texture exists and that all UV's are valid.
|
|
texturenamenodeptr = (TextureNameNode*) Texture_Name (trianglevertices [0].PatchIndex);
|
|
if (texturenamenodeptr != NULL) {
|
|
validuvs = true;
|
|
for (v = 0; v < Triangle::VERTICES_COUNT; v++) {
|
|
validuvs &= trianglevertices [v].ValidUV;
|
|
}
|
|
if (validuvs) {
|
|
triangle.TextureNameNodePtr = texturenamenodeptr;
|
|
triangle.TextureID = trianglevertices [0].PatchIndex;
|
|
triangle.Normal = trianglevertices [0].FaceNormal;
|
|
for (v = 0; v < Triangle::VERTICES_COUNT; v++) {
|
|
triangle.Vertices [v].Point = trianglevertices [v].Point;
|
|
triangle.Vertices [v].UV = trianglevertices [v].UV;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
notexture = true;
|
|
solvestatistics.Count [SolveStatistics::FACE_NO_LIGHTMAP]++;
|
|
break;
|
|
}
|
|
|
|
case FACE_NOT_FOUND:
|
|
notexture = true;
|
|
solvestatistics.Count [SolveStatistics::FACE_NOT_FOUND]++;
|
|
break;
|
|
}
|
|
|
|
if (notexture) {
|
|
|
|
// Return the original triangle with no associated texture.
|
|
triangle.Normal = normal;
|
|
for (v = 0; v < Triangle::VERTICES_COUNT; v++) {
|
|
triangle.Vertices [v].Point = points [v];
|
|
}
|
|
triangle.TextureNameNodePtr = NULL;
|
|
triangle.TextureID = notextureid;
|
|
}
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LightscapeSolve::Submit_Triangle -- *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 06/07/00 IML : Created. *
|
|
*=============================================================================================*/
|
|
void LightscapeSolve::Submit_Triangle (PackingTriangle &triangle)
|
|
{
|
|
DynamicVectorClass <Triangle> adjtriangles;
|
|
|
|
adjtriangles.Set_Growth_Step (32);
|
|
Find_Adjacent_Triangles (triangle, adjtriangles);
|
|
Submit (&triangle, adjtriangles);
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LightscapeSolve::Find_Adjacent_Triangles -- *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 6/05/00 IML : Created. *
|
|
*=============================================================================================*/
|
|
void LightscapeSolve::Find_Adjacent_Triangles (const PackingTriangle &principaltriangle, DynamicVectorClass <Triangle> &adjtriangles)
|
|
{
|
|
DynamicVectorClass <VerticesStruct*> spatialvertexset;
|
|
|
|
// For each principal triangle vertex...
|
|
for (unsigned v = 0; v < Triangle::VERTICES_COUNT; v++) {
|
|
|
|
// Find all vertices that lie within spatial tolerance of this principal triangle vertex.
|
|
spatialvertexset.Clear();
|
|
Find_Vertices (principaltriangle.Vertices [v].Point, SpatialTolerance, spatialvertexset);
|
|
for (int s = 0; s < spatialvertexset.Count(); s++) {
|
|
|
|
Vector3 normal;
|
|
float angle;
|
|
|
|
// Is the face that is associated with this spatial vertex within the smoothing angle of the principal triangle's normal?
|
|
normal = spatialvertexset [s]->FaceNormal;
|
|
angle = acosf (Vector3::Dot_Product (principaltriangle.Normal, normal));
|
|
if (angle <= SmoothingAngle) {
|
|
|
|
TextureNameNode *texturenamenodeptr;
|
|
unsigned textureid;
|
|
|
|
// Is the texture name valid?
|
|
texturenamenodeptr = (TextureNameNode*) Texture_Name (spatialvertexset [s]->PatchIndex);
|
|
textureid = spatialvertexset [s]->PatchIndex;
|
|
if (texturenamenodeptr != NULL) {
|
|
|
|
PatchFaceStruct p, *patchfaceptr;
|
|
|
|
// Find a patch face in the database with patch index that matches the patch index of the spatial vertex.
|
|
p.PatchIndex = spatialvertexset [s]->PatchIndex;
|
|
patchfaceptr = (PatchFaceStruct*) bsearch (&p, &PatchFaces [0], PatchFaces.Count(), sizeof (PatchFaceStruct), Compare_Patch_Faces);
|
|
|
|
// Does a patch exist?
|
|
if (patchfaceptr != NULL) {
|
|
|
|
// Step backwards in the database to the first patch that matches.
|
|
// NOTE: bsearch() does not necessarily return the first key in the array that matches when there are duplicate keys.
|
|
while (patchfaceptr > &PatchFaces [0]) {
|
|
if (Compare_Patch_Faces (&p, patchfaceptr - 1) != 0) break;
|
|
patchfaceptr--;
|
|
}
|
|
|
|
// Step forwards in the database while there is a patch match.
|
|
while ((patchfaceptr < &PatchFaces [0] + PatchFaces.Count()) && (Compare_Patch_Faces (&p, patchfaceptr) == 0)) {
|
|
|
|
unsigned trianglecount;
|
|
|
|
// Is this patch face a triangle or a quadrilateral?
|
|
trianglecount = (patchfaceptr->VertexIndices [PatchFaceStruct::VERTICES_PER_PATCH_FACE - 1] == PatchFaceStruct::VERTEX_INDEX_INVALID) ? 1 : 2;
|
|
|
|
// For each triangle...
|
|
for (unsigned t = 0; t < trianglecount; t++) {
|
|
|
|
const unsigned _vertexindex [2][3] = {{0, 1, 2}, {0, 2, 3}};
|
|
|
|
bool validuvs;
|
|
unsigned v;
|
|
|
|
validuvs = true;
|
|
for (v = 0; v < Triangle::VERTICES_COUNT; v++) {
|
|
validuvs &= Vertices [patchfaceptr->VertexIndices [_vertexindex [t][v]]].ValidUV;
|
|
}
|
|
|
|
if (validuvs) {
|
|
|
|
Triangle triangle;
|
|
bool hasequivalent;
|
|
|
|
triangle.TextureNameNodePtr = texturenamenodeptr;
|
|
triangle.TextureID = textureid;
|
|
triangle.Normal = normal;
|
|
for (v = 0; v < Triangle::VERTICES_COUNT; v++) {
|
|
triangle.Vertices [v].Point = Vertices [patchfaceptr->VertexIndices [_vertexindex [t][v]]].Point;
|
|
triangle.Vertices [v].UV = Vertices [patchfaceptr->VertexIndices [_vertexindex [t][v]]].UV;
|
|
}
|
|
|
|
// Linearly search the existing adjacent triangles to see if this triangle already exists.
|
|
hasequivalent = false;
|
|
for (int a = 0; a < adjtriangles.Count(); a++) {
|
|
if (triangle.Is_Equivalent (adjtriangles [a])) {
|
|
hasequivalent = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!hasequivalent) adjtriangles.Add (triangle);
|
|
}
|
|
}
|
|
|
|
// Advance to next patch face.
|
|
patchfaceptr++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LightscapeSolve::Find_Triangle -- *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 7/27/99 IML : Created. *
|
|
*=============================================================================================*/
|
|
void LightscapeSolve::Find_Triangle (const Vector3 *points, const Vector3 &facenormal, VerticesStruct *vertices, FaceResultEnum &faceresult)
|
|
{
|
|
const unsigned attemptcount = 14; // No. of binary search attempts (see note below).
|
|
|
|
ASSERT (points != NULL);
|
|
|
|
// Attempt to find the face with zero tolerance.
|
|
Find_Triangle (points, 0.0f, true, &facenormal, vertices, faceresult);
|
|
|
|
// If the face was not found then attempt to find a face by iterating over a range of
|
|
// tolerances using a binary search technique.
|
|
if (faceresult == FACE_NOT_FOUND) {
|
|
|
|
bool faceambiguous;
|
|
float maxtolerance = SpatialTolerance;
|
|
float mintolerance = 0.00f;
|
|
float tolerance;
|
|
|
|
tolerance = (maxtolerance + mintolerance) * 0.5f;
|
|
|
|
// NOTE: The tolerance will be increased or decreased, depending upon whether the
|
|
// current result is 'not found' or 'ambiguous'. On each iteration the tolerance
|
|
// range (max tolerance - min tolerance) is halved. Thus, after n iterations, the
|
|
// tolerance range will be (max tolerance - min tolerance) / 2 ^ n. I have purposely
|
|
// set n, the number of attempts, to yield a range at the limit of floating point
|
|
// accuracy.
|
|
faceambiguous = false;
|
|
for (unsigned attempt = 0; attempt < attemptcount; attempt++) {
|
|
Find_Triangle (points, tolerance, false, NULL, vertices, faceresult);
|
|
if (faceresult == FACE_AMBIGUOUS) {
|
|
|
|
// Flag that an ambiguous result occured.
|
|
faceambiguous = true;
|
|
|
|
// Decrease the tolerance.
|
|
maxtolerance = tolerance;
|
|
tolerance = (tolerance + mintolerance) * 0.5f;
|
|
} else {
|
|
if (faceresult == FACE_NOT_FOUND) {
|
|
|
|
// Increase the tolerance.
|
|
mintolerance = tolerance;
|
|
tolerance = (tolerance + maxtolerance) * 0.5f;
|
|
} else {
|
|
|
|
// The face has been found.
|
|
ASSERT (faceresult == FACE_FOUND);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If an ambiguous result occured on ANY iteration then the maximum tolerance is the
|
|
// smallest tolerance at which the ambiguity exists. Try to resolve the ambiguity
|
|
// using the centroid and normal tests.
|
|
if ((faceresult != FACE_FOUND) && faceambiguous) {
|
|
Find_Triangle (points, maxtolerance, true, &facenormal, vertices, faceresult);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LightscapeSolve::Find_Triangle -- *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 7/27/99 IML : Created. *
|
|
*=============================================================================================*/
|
|
void LightscapeSolve::Find_Triangle (const Vector3 *points, float tolerance, bool centroidtest, const Vector3 *facenormal, VerticesStruct *vertices, FaceResultEnum &faceresult)
|
|
{
|
|
const int growthstep = 256;
|
|
|
|
DynamicVectorClass <VerticesStruct*> vertexset [Triangle::VERTICES_COUNT];
|
|
DynamicVectorClass <VerticesStruct*> *candidatevertexset = &vertexset [0];
|
|
unsigned patchindex;
|
|
int n, c, v;
|
|
bool success;
|
|
|
|
ASSERT (points != NULL);
|
|
|
|
// Create a set of vertex indices that correspond to the zeroth point.
|
|
candidatevertexset->Set_Growth_Step (growthstep);
|
|
Find_Vertices (points [0], tolerance, *candidatevertexset);
|
|
|
|
// Remove any duplicate patches from the candidate set.
|
|
c = 0;
|
|
while (c < candidatevertexset->Count()) {
|
|
patchindex = (*candidatevertexset) [c]->PatchIndex;
|
|
v = c + 1;
|
|
while (v < candidatevertexset->Count()) {
|
|
if (patchindex == (*candidatevertexset) [v]->PatchIndex) {
|
|
success = candidatevertexset->Delete (v);
|
|
ASSERT (success);
|
|
} else {
|
|
v++;
|
|
}
|
|
}
|
|
c++;
|
|
}
|
|
|
|
// For all other points...
|
|
for (n = 1; n < Triangle::VERTICES_COUNT; n++) {
|
|
|
|
// Create a set of vertex indices that correspond to n'th <point, normal> pair.
|
|
vertexset [n].Set_Growth_Step (growthstep);
|
|
Find_Vertices (points [n], tolerance, vertexset [n]);
|
|
|
|
// Delete all indexes in the candidate index set that have materials that DO NOT occur in the vertex index set.
|
|
c = 0;
|
|
while (c < candidatevertexset->Count()) {
|
|
|
|
bool found;
|
|
|
|
patchindex = (*candidatevertexset) [c]->PatchIndex;
|
|
found = false;
|
|
for (v = 0; v < vertexset [n].Count(); v++) {
|
|
if (patchindex == vertexset [n][v]->PatchIndex) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
success = candidatevertexset->Delete (c);
|
|
ASSERT (success);
|
|
} else {
|
|
c++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If there is more than one candidate and the centroid test can be applied then apply it.
|
|
if ((candidatevertexset->Count() > 1) && centroidtest) {
|
|
|
|
// If there is a patch-face database...
|
|
if (PatchFaces.Count() > 0) {
|
|
|
|
// Attempt to eliminate the ambiguity by applying a centroid test.
|
|
Vector3 centroid;
|
|
|
|
centroid = points [0];
|
|
for (n = 1; n < Triangle::VERTICES_COUNT; n++) {
|
|
centroid += points [n];
|
|
}
|
|
centroid /= (float) Triangle::VERTICES_COUNT;
|
|
|
|
// For each candidate...
|
|
c = 0;
|
|
while (c < candidatevertexset->Count()) {
|
|
|
|
bool contained;
|
|
PatchFaceStruct p;
|
|
PatchFaceStruct *patchfaceptr;
|
|
unsigned trianglecount;
|
|
|
|
patchindex = (*candidatevertexset) [c]->PatchIndex;
|
|
contained = false;
|
|
|
|
// Find a face in the array that matches the patch.
|
|
p.PatchIndex = patchindex;
|
|
patchfaceptr = (PatchFaceStruct*) bsearch (&p, &PatchFaces [0], PatchFaces.Count(), sizeof (PatchFaceStruct), Compare_Patch_Faces);
|
|
|
|
if (patchfaceptr != NULL) {
|
|
|
|
// Step backwards in the array to the first patch that matches.
|
|
// NOTE: bsearch() does not necessarily return the first key in the array that matches when there are duplicate keys.
|
|
while (patchfaceptr > &PatchFaces [0]) {
|
|
if (Compare_Patch_Faces (&p, patchfaceptr - 1) != 0) break;
|
|
patchfaceptr--;
|
|
}
|
|
|
|
// Step forwards in the array while there is a patch match.
|
|
// Find all vertices whose points and normals approximately match the requested normal and add their patch indices to the patch array.
|
|
while ((patchfaceptr < &PatchFaces [0] + PatchFaces.Count()) && (Compare_Patch_Faces (&p, patchfaceptr) == 0)) {
|
|
|
|
// NOTE: The candidate point may not match the any of the material face
|
|
// points because Lightscape may have changed the topology. Thus it is
|
|
// necessary to search ALL of the faces that have this material.
|
|
|
|
// For each triangle in the patch face...
|
|
// NOTE: Define the first triangle to be the points 0-1-2. If the patch face is a quadrilateral,
|
|
// then define the second triangle to be the points 0-2-3.
|
|
trianglecount = (patchfaceptr->VertexIndices [3] == PatchFaceStruct::VERTEX_INDEX_INVALID) ? 1 : 2;
|
|
for (unsigned t = 0; t < trianglecount; t++) {
|
|
|
|
// Create a triangle from the material face and test it against the centroid for containment.
|
|
TriClass triangle;
|
|
Vector3 normal;
|
|
|
|
triangle.V [0] = &Vertices [patchfaceptr->VertexIndices [0]].Point;
|
|
triangle.V [1] = &Vertices [patchfaceptr->VertexIndices [1 + t]].Point;
|
|
triangle.V [2] = &Vertices [patchfaceptr->VertexIndices [2 + t]].Point;
|
|
triangle.N = &normal;
|
|
triangle.Compute_Normal();
|
|
|
|
if (triangle.Contains_Point (centroid)) {
|
|
contained = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (contained) break;
|
|
|
|
// Advance to next patch face.
|
|
patchfaceptr++;
|
|
}
|
|
}
|
|
|
|
if (!contained) {
|
|
success = candidatevertexset->Delete (c);
|
|
ASSERT (success);
|
|
} else {
|
|
c++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// If there is still more than one candidate and the normal exists then apply the normal test.
|
|
if ((candidatevertexset->Count() > 1) && (facenormal != NULL)) {
|
|
|
|
float largestdp;
|
|
|
|
// Find the smallest angle (largest dot product) between the vertex face normal and the target face normal.
|
|
largestdp = (*facenormal) * ((*candidatevertexset) [0]->FaceNormal);
|
|
for (c = 1; c < candidatevertexset->Count(); c++) {
|
|
largestdp = MAX (largestdp, (*facenormal) * ((*candidatevertexset) [c]->FaceNormal));
|
|
}
|
|
|
|
// Delete all candidates that do not have the largest dot product.
|
|
c = 0;
|
|
while (c < candidatevertexset->Count()) {
|
|
|
|
float dp = (*facenormal) * ((*candidatevertexset) [c]->FaceNormal);
|
|
|
|
if (dp < largestdp) {
|
|
success = candidatevertexset->Delete (c);
|
|
ASSERT (success);
|
|
} else {
|
|
c++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now if there are no candidates then return failure.
|
|
if (candidatevertexset->Count() == 0) {
|
|
faceresult = FACE_NOT_FOUND;
|
|
} else {
|
|
|
|
unsigned candidateindex = 0;
|
|
|
|
// NOTE: If there is >1 candidate at this point the result is still ambiguous.
|
|
// If the face normal has been supplied, select the candidate with closest
|
|
// face normal - otherwise select the zeroth candidate.
|
|
if (facenormal != NULL) {
|
|
|
|
float mindistance = FLT_MAX;
|
|
|
|
for (n = 0; n < candidatevertexset->Count(); n++) {
|
|
|
|
float distance;
|
|
|
|
distance = Manhatten_Distance (*facenormal - (*candidatevertexset) [n]->FaceNormal);
|
|
if (distance < mindistance) {
|
|
mindistance = distance;
|
|
candidateindex = n;
|
|
}
|
|
}
|
|
}
|
|
|
|
// For the zeroth point...
|
|
patchindex = (*candidatevertexset) [candidateindex]->PatchIndex;
|
|
vertices [0] = *((*candidatevertexset) [candidateindex]);
|
|
|
|
// For all other points...
|
|
for (n = 1; n < Triangle::VERTICES_COUNT; n++) {
|
|
|
|
bool assigned = false;
|
|
|
|
for (v = 0; v < vertexset [n].Count(); v++) {
|
|
if (vertexset [n][v]->PatchIndex == patchindex) {
|
|
vertices [n] = *(vertexset [n][v]);
|
|
assigned = true;
|
|
break;
|
|
}
|
|
}
|
|
ASSERT (assigned);
|
|
}
|
|
|
|
// Flag if the result was ambiguous.
|
|
if (candidatevertexset->Count() > 1) {
|
|
faceresult = FACE_AMBIGUOUS;
|
|
} else {
|
|
|
|
// There must be exactly one candidate.
|
|
ASSERT (candidatevertexset->Count() == 1);
|
|
faceresult = FACE_FOUND;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LightscapeSolve::Find_Vertices -- *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 7/27/99 IML : Created. *
|
|
*=============================================================================================*/
|
|
void LightscapeSolve::Find_Vertices (const Vector3 &point, float tolerance, DynamicVectorClass <VerticesStruct*> &vertices)
|
|
{
|
|
if (VertexIndices.Count() > 0) {
|
|
|
|
unsigned *vertexindexptr = NULL;
|
|
int b0, b1, b;
|
|
float x;
|
|
|
|
// There should be a 1-1 mapping between vertex indices and vertices.
|
|
ASSERT (VertexIndices.Count() == Vertices.Count());
|
|
|
|
// Find a vertex that matches point to within tolerance using binary search technique.
|
|
b0 = 0;
|
|
b1 = VertexIndices.Count() - 1;
|
|
do {
|
|
|
|
b = (b0 + b1) >> 1;
|
|
x = Vertices [VertexIndices [b]].Point.X;
|
|
if (point.X - x < -tolerance) {
|
|
b1 = b - 1;
|
|
} else {
|
|
if (point.X - x > tolerance) {
|
|
b0 = b + 1;
|
|
} else {
|
|
vertexindexptr = &VertexIndices [b];
|
|
break;
|
|
}
|
|
}
|
|
|
|
} while (b0 <= b1);
|
|
|
|
// Step backwards in the array to the first vertex that meets the match criteria defined by Compare_Vertices().
|
|
// NOTE: Above search did not necessarily return the first key in the array that matches when there are duplicate keys.
|
|
if (vertexindexptr != NULL) {
|
|
while (vertexindexptr > &VertexIndices [0]) {
|
|
x = Vertices [*(vertexindexptr - 1)].Point.X;
|
|
if (point.X - x > tolerance) break;
|
|
vertexindexptr--;
|
|
}
|
|
|
|
// Step forwards in the array while there is an approximate vertex match.
|
|
// Add all vertices that are valid and whose points match the requested point within tolerance.
|
|
while (vertexindexptr < &VertexIndices [0] + VertexIndices.Count()) {
|
|
|
|
VerticesStruct *vertexptr;
|
|
|
|
vertexptr = &Vertices [*vertexindexptr];
|
|
if (point.X - vertexptr->Point.X < -tolerance) break;
|
|
if (Manhatten_Distance (vertexptr->Point - point) <= tolerance) {
|
|
vertices.Add (vertexptr);
|
|
}
|
|
|
|
// Advance to next vertex index.
|
|
vertexindexptr++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LightscapeSolve::Face_Size -- *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 9/17/99 IML : Created. *
|
|
*=============================================================================================*/
|
|
LightscapeSolve::FaceSizeEnum LightscapeSolve::Face_Size (const Vector3 *points, unsigned count)
|
|
{
|
|
const float undersizedarea = 0.0016f; // An area << the area of a lightmap lumel (assuming
|
|
// that lumel density is less than 625 lumels/metre).
|
|
float area;
|
|
|
|
// Test for degeneracy.
|
|
// NOTE: Assume that the points are ordered (for triangles this is guaranteed).
|
|
for (unsigned n = 0; n < count; n++) {
|
|
if (points [n] == points [(n + 1) % count]) return (FACE_DEGENERATE);
|
|
}
|
|
|
|
// Currently, the area calculation requires that the polygon is a triangle.
|
|
ASSERT (count == 3);
|
|
|
|
// Test for a small area.
|
|
area = 0.5f * Vector3::Cross_Product (points [1] - points [0], points [2] - points [0]).Length();
|
|
if (area < undersizedarea) return (FACE_UNDERSIZED);
|
|
return (FACE_NOT_UNDERSIZED);
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LightscapeSolve::Longest_Edge -- *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 9/17/99 IML : Created. *
|
|
*=============================================================================================*/
|
|
unsigned LightscapeSolve::Longest_Edge (const Vector3 *points, unsigned count, bool *usededges)
|
|
{
|
|
float maxlength = -1.0f;
|
|
float length;
|
|
unsigned edgeindex;
|
|
|
|
for (unsigned e = 0; e < count; e++) {
|
|
if (!usededges [e]) {
|
|
length = (points [(e + 1) % count] - points [e]).Length();
|
|
if (length > maxlength) {
|
|
maxlength = length;
|
|
edgeindex = e;
|
|
}
|
|
}
|
|
}
|
|
|
|
// An edge must have been found.
|
|
ASSERT (maxlength >= 0.0f);
|
|
usededges [edgeindex] = true;
|
|
return (edgeindex);
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LightscapeSolve::Texture_Name -- *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 7/27/99 IML : Created. *
|
|
*=============================================================================================*/
|
|
const TextureNameNode *LightscapeSolve::Texture_Name (unsigned patchindex)
|
|
{
|
|
// If the array element does not exist yet then the name has not been set.
|
|
if ((int) patchindex >= TextureNames.Length()) return (NULL);
|
|
return TextureNames [patchindex];
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LightscapeSolve::Compare_Material_Texture_Names -- *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 7/27/99 IML : Created. *
|
|
*=============================================================================================*/
|
|
int LightscapeSolve::Compare_Material_Texture_Names (const void *materialname0, const void *materialname1)
|
|
{
|
|
return (strcmp (*((const char**) materialname0), *((const char**) materialname1)));
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LightscapeSolve::Compare_Vertices *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 7/27/99 IML : Created. *
|
|
*=============================================================================================*/
|
|
int LightscapeSolve::Compare_Vertices (const void *vertexindexptr0, const void *vertexindexptr1)
|
|
{
|
|
float x0, x1;
|
|
|
|
ASSERT (_ActiveImporter != NULL);
|
|
|
|
x0 = _ActiveImporter->Vertices [*((unsigned*) vertexindexptr0)].Point.X;
|
|
x1 = _ActiveImporter->Vertices [*((unsigned*) vertexindexptr1)].Point.X;
|
|
|
|
if (x0 < x1) {
|
|
return (-1);
|
|
} else {
|
|
if (x0 > x1) {
|
|
return (1);
|
|
} else {
|
|
return (0);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LightscapeSolve::Compare_Patch_Faces -- *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 9/17/99 IML : Created. *
|
|
*=============================================================================================*/
|
|
int LightscapeSolve::Compare_Patch_Faces (const void *patchface0, const void *patchface1)
|
|
{
|
|
unsigned patchindex0 = ((PatchFaceStruct*) patchface0)->PatchIndex;
|
|
unsigned patchindex1 = ((PatchFaceStruct*) patchface1)->PatchIndex;
|
|
|
|
if (patchindex0 < patchindex1) {
|
|
return (-1);
|
|
} else {
|
|
if (patchindex0 > patchindex1) {
|
|
return (1);
|
|
} else {
|
|
return (0);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LightscapeSolve::Pack -- *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 09/07/00 IML : Created. *
|
|
*=============================================================================================*/
|
|
void LightscapeSolve::Pack()
|
|
{
|
|
LightmapPacker::Pack (ProceduralTexture);
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LsInformationFactory::OnGet*Builder -- *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 9/16/99 IML : Created. *
|
|
*=============================================================================================*/
|
|
LtTInfoBuilderApi* LsInformationFactory::OnGetInfoBuilder()
|
|
{
|
|
return (new LsInfoBuilder (this));
|
|
}
|
|
|
|
LtTParameterBuilderApi* LsInformationFactory::OnGetParameterBuilder()
|
|
{
|
|
return (new LsParameterBuilder (this));
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LsPreparationFactory::OnGet*Builder -- *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 7/27/99 IML : Created. *
|
|
*=============================================================================================*/
|
|
LtTInfoBuilderApi *LsPreparationFactory::OnGetInfoBuilder()
|
|
{
|
|
return (new LsInfoBuilder (this));
|
|
}
|
|
|
|
LtTParameterBuilderApi *LsPreparationFactory::OnGetParameterBuilder()
|
|
{
|
|
return (new LsParameterBuilder (this));
|
|
}
|
|
|
|
LtTMaterialBuilderApi *LsPreparationFactory::OnGetMaterialBuilder()
|
|
{
|
|
return (new LsMaterialBuilder (this));
|
|
}
|
|
|
|
LtTLampBuilderApi *LsPreparationFactory::OnGetLampBuilder()
|
|
{
|
|
return (new LsLampBuilder (this));
|
|
}
|
|
|
|
LtTMeshBuilderApi *LsPreparationFactory::OnGetMeshBuilder()
|
|
{
|
|
return (new LsMeshInquirer (this));
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LsMainFactory::LsMainFactory -- *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 7/23/99 IML : Created. *
|
|
*=============================================================================================*/
|
|
LsMainFactory::LsMainFactory()
|
|
: LtTBuilderFactory()
|
|
{
|
|
PatchFaceIndex = 0;
|
|
VertexIndex = 0;
|
|
PatchIndex = 0;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LsMainFactory::OnGet*Builder -- *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 7/23/99 IML : Created. *
|
|
*=============================================================================================*/
|
|
LtTInfoBuilderApi* LsMainFactory::OnGetInfoBuilder()
|
|
{
|
|
return (new LsInfoBuilder (this));
|
|
}
|
|
|
|
LtTParameterBuilderApi* LsMainFactory::OnGetParameterBuilder()
|
|
{
|
|
return (new LsParameterBuilder (this));
|
|
}
|
|
|
|
LtTMeshBuilderApi* LsMainFactory::OnGetMeshBuilder()
|
|
{
|
|
return (new LsMeshBuilder (this));
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LsInfoBuilder::LsInfoBuilder -- *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 9/17/99 IML : Created. *
|
|
*=============================================================================================*/
|
|
LsInfoBuilder::LsInfoBuilder (LtTBuilderFactory *factory)
|
|
: LtTBaseInfoBuilder (factory)
|
|
{
|
|
// Do any special initialization here.
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LsInfoBuilder::Finish -- *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 9/17/99 IML : Created. *
|
|
*=============================================================================================*/
|
|
LtTBool LsInfoBuilder::Finish()
|
|
{
|
|
// Read any info values here.
|
|
LightscapeSolve::Importer()->Set_Is_Solution (IsSolution() ? true : false);
|
|
LightscapeSolve::Importer()->Set_Patch_Cluster_Count (GetNumGroups());
|
|
LightscapeSolve::Importer()->Set_Brightness (GetBrightness());
|
|
LightscapeSolve::Importer()->Set_Contrast (GetContrast());
|
|
|
|
// Return success.
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LsParameterBuilder::LsParameterBuilder -- *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 9/17/99 IML : Created. *
|
|
*=============================================================================================*/
|
|
LsParameterBuilder::LsParameterBuilder (LtTBuilderFactory *factory)
|
|
: LtTBaseParameterBuilder (factory)
|
|
{
|
|
// Do any special initialization here.
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LsParameterBuilder::Finish -- *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 9/17/99 IML : Created. *
|
|
*=============================================================================================*/
|
|
LtTBool LsParameterBuilder::Finish()
|
|
{
|
|
// Read any parameter values here.
|
|
LightscapeSolve::Importer()->Set_Is_Daylight ((GetFlags() & LT_PROCESS_DAYLIGHT) != 0);
|
|
LightscapeSolve::Importer()->Set_Is_Exterior ((GetFlags() & LT_PROCESS_EXTERIOR) != 0);
|
|
|
|
// Return success.
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LsMateriaBuilder::LsMaterialBuilder -- *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 7/27/99 IML : Created. *
|
|
*=============================================================================================*/
|
|
LsMaterialBuilder::LsMaterialBuilder (LtTBuilderFactory *factory)
|
|
: LtTBaseMaterialBuilder (factory)
|
|
{
|
|
// Do any special initialization here.
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LsMateriaBuilder::Finish -- *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 7/27/99 IML : Created. *
|
|
*=============================================================================================*/
|
|
LtTBool LsMaterialBuilder::Finish()
|
|
{
|
|
ASSERT (LightscapeSolve::Importer() != NULL);
|
|
LightscapeSolve::Importer()->Add_Material_Texture_Name (GetName(), GetTextureName());
|
|
|
|
// Return success.
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LsLampBuilder::LsLampBuilder -- *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 3/24/00 IML : Created. *
|
|
*=============================================================================================*/
|
|
LsLampBuilder::LsLampBuilder (LtTBuilderFactory *factory)
|
|
: LtTBaseLampBuilder (factory)
|
|
{
|
|
// Do any special initialization here.
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LsLampBuilder::Finish -- *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 3/24/00 IML : Created. *
|
|
*=============================================================================================*/
|
|
LtTBool LsLampBuilder::Finish()
|
|
{
|
|
const char *unknownlighttext =
|
|
"There is a light in the solve with unknown type.";
|
|
|
|
const Vector3 oodistancesquaredfactors (0.0f, 0.0f, 1.0f);
|
|
const Vector3 zaxis (0.0f, 0.0f, 1.0f);
|
|
const ColorVector black (0.0f, 0.0f, 0.0f);
|
|
const ColorVector white (1.0f, 1.0f, 1.0f);
|
|
|
|
float dir [3][3];
|
|
LightClass *light;
|
|
Vector3 position;
|
|
ColorVector color;
|
|
Matrix3D transform;
|
|
|
|
ASSERT (LightscapeSolve::Importer() != NULL);
|
|
|
|
// If the light name begins with a '@' skip it - it is not intended for export.
|
|
if (LightscapeSolve::Importer()->Light_Exclusion_String() != NULL) {
|
|
if (strstr (GetName(), LightscapeSolve::Importer()->Light_Exclusion_String())) return (TRUE);
|
|
}
|
|
|
|
// Skip skylight - it cannot be meaningfully translated into a LightClass object.
|
|
if ((GetType() == LT_LAMP_DAYLIGHT) && (strcmp (GetName(), "Skylight") == 0)) return (TRUE);
|
|
|
|
// If the lamp does not store direct illumination and has not been raytraced then
|
|
// it has not been included in the solve - skip it.
|
|
if ((GetFlags() & LT_LAMP_CAST_NO_DIRECT) && !(GetFlags() & LT_LAMP_RAY_TRACE_DIRECT)) return (TRUE);
|
|
|
|
// If this light has non-zero intensity then add the light to the solve database.
|
|
if (GetIntensity() > 0.0f) {
|
|
|
|
// NOTE 0: Lightscape uses a right-handed coordinate system.
|
|
// NOTE 1: For directional lights (including sunlight), multiplying the direction matrix
|
|
// by (0, 0, 1) will yield the direction of the light rays.
|
|
GetDirection (dir);
|
|
Matrix3 direction (dir [0][0], dir [0][1], dir [0][2],
|
|
dir [1][0], dir [1][1], dir [1][2],
|
|
dir [2][0], dir [2][1], dir [2][2]);
|
|
|
|
// Create light of appropriate type.
|
|
switch (GetType()) {
|
|
|
|
case LT_LAMP_ISOTROPIC: // Spherical equal intensity distribution.
|
|
|
|
light = new LightClass (LightClass::POINT);
|
|
ASSERT (light != NULL);
|
|
break;
|
|
|
|
case LT_LAMP_DIFFUSE: // Hemispherical distribution with intensity proportional to the cosine to the direction of light.
|
|
|
|
// Interpret this light as a spotlight with a 180 degree spot angle.
|
|
light = new LightClass (LightClass::SPOT);
|
|
ASSERT (light != NULL);
|
|
light->Set_Spot_Angle (WWMATH_PI);
|
|
light->Set_Spot_Direction (zaxis);
|
|
|
|
// For efficiency, use unity as exponent.
|
|
light->Set_Spot_Exponent (1.0f);
|
|
break;
|
|
|
|
case LT_LAMP_SPOTLIGHT: // Spotlight distribution. Intensity at beam angle is one-half maximum intensity. Intensity is truncated to 0 at field angle.
|
|
|
|
light = new LightClass (LightClass::SPOT);
|
|
ASSERT (light != NULL);
|
|
|
|
// NOTE: Field angle is defined as angle swept from center of beam to outside.
|
|
light->Set_Spot_Angle (GetFieldAngle() * 2.0f);
|
|
light->Set_Spot_Direction (zaxis);
|
|
|
|
// For efficiency, use unity as exponent.
|
|
light->Set_Spot_Exponent (1.0f);
|
|
break;
|
|
|
|
case LT_LAMP_GENERAL: // General distribution is determined from table if intensities in given directions.
|
|
|
|
{
|
|
float angle;
|
|
|
|
// Get the widest angle - this is normally, but not always, the field angle.
|
|
angle = MAX (GetBeamAngle(), GetFieldAngle());
|
|
if (angle > WWMATH_PI / 2.0f) {
|
|
|
|
// Interpret light as an isometric light.
|
|
light = new LightClass (LightClass::POINT);
|
|
ASSERT (light != NULL);
|
|
|
|
} else {
|
|
|
|
// Interpret light as a spotlight.
|
|
light = new LightClass (LightClass::SPOT);
|
|
ASSERT (light != NULL);
|
|
|
|
light->Set_Spot_Angle (angle * 2.0f);
|
|
light->Set_Spot_Direction (zaxis);
|
|
|
|
// For efficiency, use unity as exponent.
|
|
light->Set_Spot_Exponent (1.0f);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case LT_LAMP_DAYLIGHT: // Special value used to flag daylight when import an LS file.
|
|
|
|
light = new LightClass (LightClass::DIRECTIONAL);
|
|
ASSERT (light != NULL);
|
|
break;
|
|
|
|
default:
|
|
ASSERT (FALSE);
|
|
throw (unknownlighttext);
|
|
break;
|
|
}
|
|
|
|
// Set transform matrix that defines direction and position of light source.
|
|
position.Set (GetLocation().GetX(), GetLocation().GetY(), GetLocation().GetZ());
|
|
transform.Set (direction, position);
|
|
light->Set_Transform (transform);
|
|
|
|
// Set intensity.
|
|
light->Set_Intensity (1.0f);
|
|
|
|
RadianceMap radiancemap (LightscapeSolve::Importer()->Get_Brightness(), LightscapeSolve::Importer()->Get_Contrast(), LightscapeSolve::Importer()->Is_Daylight(), LightscapeSolve::Importer()->Is_Exterior());
|
|
float i = GetIntensity();
|
|
LtTRGBColor irradiance (i * GetFilterColor().GetR(), i * GetFilterColor().GetG(), i * GetFilterColor().GetB());
|
|
|
|
// Set colors.
|
|
color.Set (irradiance, radiancemap);
|
|
|
|
// Clamp to white.
|
|
color.Update_Min (white);
|
|
|
|
light->Set_Ambient (black);
|
|
light->Set_Diffuse (color);
|
|
light->Set_Specular (color);
|
|
|
|
// Set attenuation model.
|
|
if (GetType() != LT_LAMP_DAYLIGHT) {
|
|
|
|
const float oocutoff = 1.0f / 0.025f; // Reciprocal of light intensity at far attenuation.
|
|
|
|
// NOTE: Convert Lightscape 1/d^2 attenuation to an equivalent MAX type light.
|
|
light->Set_Flag (LightClass::NEAR_ATTENUATION, false);
|
|
light->Set_Near_Attenuation_Range (0.0f, 0.0f);
|
|
light->Set_Flag (LightClass::FAR_ATTENUATION, true);
|
|
|
|
// Calculate attenuation range.
|
|
// The far attenuation is calculated as the distance at which intensity falls to a given
|
|
// ratio (cut-off), assuming an attenuation of c/d^2, where c = monochrome intensity of light source.
|
|
float d = sqrt (oocutoff * color.Length());
|
|
light->Set_Far_Attenuation_Range (d * 0.25f, d);
|
|
|
|
} else {
|
|
|
|
light->Set_Flag (LightClass::NEAR_ATTENUATION, false);
|
|
light->Set_Flag (LightClass::FAR_ATTENUATION, false);
|
|
}
|
|
|
|
// Miscellaneous attributes.
|
|
light->Enable_Shadows ((GetFlags() & LT_LAMP_CAST_NO_SHADOWS) ? false : true);
|
|
|
|
LightscapeSolve::Importer()->Add_Light (light);
|
|
|
|
// Clean-up.
|
|
light->Release_Ref();
|
|
}
|
|
|
|
// Return success.
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LsMeshInquirer::LsMeshInquirer -- *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 04/19/00 IML : Created. *
|
|
*=============================================================================================*/
|
|
LsMeshInquirer::LsMeshInquirer (LtTBuilderFactory *factory)
|
|
: LtTBaseMeshBuilder (factory)
|
|
{
|
|
// Assume initially that the solve is NOT an M2T solve.
|
|
LightscapeSolve::Importer()->Set_Is_M2T_Solve (false);
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LsMeshInquirer::LsMeshInquirer -- *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 04/19/00 IML : Created. *
|
|
*=============================================================================================*/
|
|
LtTBool LsMeshInquirer::Finish()
|
|
{
|
|
if (GetProperties() & LT_MESH_RAWTEXTURE) {
|
|
LightscapeSolve::Importer()->Set_Is_M2T_Solve (true);
|
|
}
|
|
|
|
// Return success.
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LsMeshBuilder::LsMeshBuilder -- *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 7/23/99 IML : Created. *
|
|
*=============================================================================================*/
|
|
LsMeshBuilder::LsMeshBuilder (LtTBuilderFactory *factory)
|
|
: LtTBaseMeshBuilder (factory)
|
|
{
|
|
ASSERT (strcmp (typeid (*factory).name(), "class LsMainFactory") == 0);
|
|
FactoryPtr = (LsMainFactory*) factory;
|
|
Faces = NULL;
|
|
FaceCount = 0;
|
|
AllocatedCount = 0;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LsMeshBuilder::~LsMeshBuilder -- *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 9/16/99 IML : Created. *
|
|
*=============================================================================================*/
|
|
LsMeshBuilder::~LsMeshBuilder()
|
|
{
|
|
if (Faces != NULL) delete [] Faces;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LsMeshBuilder::SetFaces -- *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 9/16/99 IML : Created. *
|
|
*=============================================================================================*/
|
|
void LsMeshBuilder::SetFaces (const int facecount, const LtTFace *faces)
|
|
{
|
|
int topcount, f, g;
|
|
|
|
// Count the number of faces in the top-level hierarchy.
|
|
topcount = 0;
|
|
for (f = 0; f < facecount; f++) {
|
|
topcount++;
|
|
f += faces [f].GetDescendents();
|
|
}
|
|
|
|
// If the block allocated for faces is not big enough delete it and allocate a new one.
|
|
if (topcount > AllocatedCount) {
|
|
if (Faces != NULL) delete [] Faces;
|
|
Faces = new LtTFace [topcount];
|
|
ASSERT (Faces != NULL);
|
|
AllocatedCount = topcount;
|
|
}
|
|
|
|
// Copy the faces in the top-level hierarchy into the block allocated for faces.
|
|
g = 0;
|
|
for (f = 0; f < facecount; f++) {
|
|
Faces [g] = faces [f];
|
|
g++;
|
|
f += faces [f].GetDescendents();
|
|
}
|
|
|
|
ASSERT (g == topcount);
|
|
FaceCount = topcount;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LsMeshBuilder::GetFaceCount -- *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 9/16/99 IML : Created. *
|
|
*=============================================================================================*/
|
|
int LsMeshBuilder::GetFaceCount() const
|
|
{
|
|
return (FaceCount);
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LsMeshBuilder::GetFaces -- *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 9/16/99 IML : Created. *
|
|
*=============================================================================================*/
|
|
const LtTFace* LsMeshBuilder::GetFaces() const
|
|
{
|
|
return (Faces);
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LsMeshBuilder::Finish -- Called by the Lightscape framework after each patch cluster read. *
|
|
* *
|
|
* Called by the Lightscape framework after each patch cluster read from a .ls file. The file *
|
|
* read is initiated by a LtSolutionImport() call. The clustering and the mesh structure is *
|
|
* entirely under the control of Lightscape. Each patch cluster may or may not reference a *
|
|
* texture and may or may not contain a radiosity mesh. If the .ls file has been produced by *
|
|
* Lightscape's 'Mesh to Texture' tool (M2T) with the "Generate Illumination Map" option *
|
|
* enabled then the material definition will (should) reference a lightmap texture. *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 7/23/99 IML : Created. *
|
|
*=============================================================================================*/
|
|
LtTBool LsMeshBuilder::Finish()
|
|
{
|
|
const char *invalidmappingtext =
|
|
"There is a face in the model with an invalid texture mapping. Please ensure that the\
|
|
Mesh to Texture option called 'Convert each surface to a single texture per surface' was\
|
|
used.";
|
|
|
|
const char *notnormalizedtext =
|
|
"There is a vertex in the model with a valid texture mapping but a non-normalized texture\
|
|
coordinate.";
|
|
|
|
const char *unmatchedmodeltext =
|
|
"The solve files do not contain the same model information. For any 2 solve files, the first\
|
|
n patches must match, where n is the smallest patch count of the two files.";
|
|
|
|
VertexUser usedvertices (*this);
|
|
bool success;
|
|
|
|
ASSERT (LightscapeSolve::Importer() != NULL);
|
|
|
|
// Add the vertices to the Lightscape solve database.
|
|
// If this mesh is an M2T patch...
|
|
if (GetProperties() & LT_MESH_RAWTEXTURE) {
|
|
|
|
int vertexindex;
|
|
LtTVector lsv; // Lightscape scratch vertex.
|
|
Vector3 v; // Scratch vertex.
|
|
Vector3 origin, du, dv; // Definition of texture projection.
|
|
Vector2 length; //
|
|
bool normalized;
|
|
LtTUnitVector lsn;
|
|
Vector3 n;
|
|
|
|
// Lightscape definition of texture projection.
|
|
LtTTextureProjection lsprojection = GetTextureProjection();
|
|
|
|
// If the texture mapping is invalid (ie. not orthographic)...
|
|
if ((lsprojection.GetFlags() & LT_PROJECTION_TYPE_MASK) != LT_PROJECTION_ORTHOGRAPHIC) {
|
|
throw (invalidmappingtext);
|
|
}
|
|
|
|
// Define origin and texture vectors for a planar mapping.
|
|
lsv = lsprojection.GetOrigin();
|
|
origin.Set (lsv.GetX(), lsv.GetY(), lsv.GetZ());
|
|
lsv = lsprojection.GetDu();
|
|
du.Set (lsv.GetX(), lsv.GetY(), lsv.GetZ());
|
|
lsv = lsprojection.GetDv();
|
|
dv.Set (lsv.GetX(), lsv.GetY(), lsv.GetZ());
|
|
|
|
// Normalize the texture vectors.
|
|
length.U = du.Length();
|
|
length.V = dv.Length();
|
|
du /= length.U;
|
|
dv /= length.V;
|
|
|
|
// Step thru the vertices, calculate UV from the texture projection and add the information
|
|
// to the Lightscape solve database.
|
|
GetPlane (lsn);
|
|
n.Set (lsn.GetX(), lsn.GetY(), lsn.GetZ());
|
|
for (vertexindex = 0; vertexindex < GetVertexCount(); vertexindex++) {
|
|
|
|
// Add only if the vertex is referenced by a face.
|
|
if (usedvertices.Is_Used (vertexindex)) {
|
|
|
|
LtTPoint lsp = GetVertices() [vertexindex];
|
|
|
|
Vector3 p (lsp.GetX(), lsp.GetY(), lsp.GetZ());
|
|
Vector2 t;
|
|
|
|
v = p - origin;
|
|
t.U = (du * v) / length.U;
|
|
t.V = (dv * v) / length.V;
|
|
|
|
// UV's must be normalized (in the range 0..1) in order to reference the lightmap properly.
|
|
normalized = ((t.U >= 0.0f) && (t.U <= 1.0f));
|
|
normalized &= ((t.V >= 0.0f) && (t.V <= 1.0f));
|
|
if (!normalized) throw (notnormalizedtext);
|
|
|
|
// Assign an index for this vertex which will uniquely identify it in the vertex database.
|
|
usedvertices.Set_Index (vertexindex, FactoryPtr->Vertex_Count());
|
|
|
|
// Add the vertex to the database.
|
|
success = LightscapeSolve::Importer()->Add_Vertex (FactoryPtr->Vertex_Count(), p, n, FactoryPtr->Patch_Count(), t);
|
|
if (!success) throw (unmatchedmodeltext);
|
|
|
|
// Advance vertex index counter.
|
|
FactoryPtr->Vertex_Increment();
|
|
}
|
|
}
|
|
|
|
// Find the texture name that corresponds to the material name and add it for this patch.
|
|
LightscapeSolve::Importer()->Add_Texture_Name (FactoryPtr->Patch_Count(), GetMaterial());
|
|
|
|
} else {
|
|
|
|
const double filtersharpness = LightscapeSolve::Importer()->Filter_Sharpness();
|
|
|
|
ProceduralTexture *blendtexture;
|
|
DynamicVectorClass <FaceVertexStruct> facevertices;
|
|
double *distanceratios;
|
|
int vertexindex;
|
|
int facevertexindex;
|
|
RadianceMap radiancemap (LightscapeSolve::Importer()->Get_Brightness(), LightscapeSolve::Importer()->Get_Contrast(), LightscapeSolve::Importer()->Is_Daylight(), LightscapeSolve::Importer()->Is_Exterior());
|
|
LtTPoint lsp;
|
|
LtTRGBColor irradiance;
|
|
LtTUnitVector lsn;
|
|
Vector3 n;
|
|
|
|
blendtexture = LightscapeSolve::Importer()->Procedural_Texture();
|
|
|
|
// NOTE: This mesh is not an M2T patch so it must instead contain valid vertex color information.
|
|
// Build an array of face vertices initialized to their respective vertex colors.
|
|
for (vertexindex = 0; vertexindex < GetVertexCount(); vertexindex++) {
|
|
if (usedvertices.Is_Used (vertexindex)) {
|
|
|
|
FaceVertexStruct facevertex;
|
|
|
|
lsp = GetVertices() [vertexindex];
|
|
irradiance = GetIrradiances() [vertexindex];
|
|
facevertex.Point.Set (lsp.GetX(), lsp.GetY(), lsp.GetZ());
|
|
facevertex.Color.Set (irradiance, radiancemap);
|
|
|
|
// Should the vertex color be blended with a sample from a procedural texture?
|
|
if (blendtexture != NULL) {
|
|
facevertex.Color *= blendtexture->Value (facevertex.Point);
|
|
}
|
|
|
|
facevertex.Weight = 1.0f;
|
|
facevertices.Add (facevertex);
|
|
}
|
|
}
|
|
|
|
// There must be at least one face vertex.
|
|
ASSERT (facevertices.Count() > 0);
|
|
|
|
// For every non-face vertex calculate its color contribution to each face vertex.
|
|
distanceratios = new double [facevertices.Count()];
|
|
ASSERT (distanceratios != NULL);
|
|
|
|
for (vertexindex = 0; vertexindex < GetVertexCount(); vertexindex++) {
|
|
if (!usedvertices.Is_Used (vertexindex)) {
|
|
|
|
Vector3 point;
|
|
ColorVector color;
|
|
double d, s;
|
|
|
|
lsp = GetVertices() [vertexindex];
|
|
irradiance = GetIrradiances() [vertexindex];
|
|
point.Set (lsp.GetX(), lsp.GetY(), lsp.GetZ());
|
|
color.Set (irradiance, radiancemap);
|
|
|
|
// Should the vertex color be blended with a sample from a procedural texture?
|
|
if (blendtexture != NULL) {
|
|
color *= blendtexture->Value (point);
|
|
}
|
|
|
|
d = (point - facevertices [0].Point).Length();
|
|
distanceratios [0] = s = 1.0f;
|
|
for (facevertexindex = 1; facevertexindex < facevertices.Count(); facevertexindex++) {
|
|
distanceratios [facevertexindex] = d / (point - facevertices [facevertexindex].Point).Length();
|
|
|
|
// Raise the distance ratio to the user specified filter sharpness.
|
|
distanceratios [facevertexindex] = pow (distanceratios [facevertexindex], filtersharpness);
|
|
|
|
s += distanceratios [facevertexindex];
|
|
}
|
|
|
|
for (facevertexindex = 0; facevertexindex < facevertices.Count(); facevertexindex++) {
|
|
|
|
float weight;
|
|
|
|
weight = distanceratios [facevertexindex] / s;
|
|
facevertices [facevertexindex].Color += color * weight;
|
|
facevertices [facevertexindex].Weight += weight;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add the face vertices to the solve database.
|
|
GetPlane (lsn);
|
|
n.Set (lsn.GetX(), lsn.GetY(), lsn.GetZ());
|
|
facevertexindex = 0;
|
|
for (vertexindex = 0; vertexindex < GetVertexCount(); vertexindex++) {
|
|
if (usedvertices.Is_Used (vertexindex)) {
|
|
|
|
ColorVector c = facevertices [facevertexindex].Color;
|
|
c /= facevertices [facevertexindex].Weight;
|
|
|
|
// Assign an index for this vertex which will uniquely identify it in the vertex database.
|
|
usedvertices.Set_Index (vertexindex, FactoryPtr->Vertex_Count());
|
|
|
|
// Add the vertex to the database.
|
|
success = LightscapeSolve::Importer()->Add_Vertex (FactoryPtr->Vertex_Count(), facevertices [facevertexindex].Point, n, FactoryPtr->Patch_Count(), c);
|
|
if (!success) {
|
|
delete [] distanceratios;
|
|
throw (unmatchedmodeltext);
|
|
}
|
|
|
|
// Advance the vertex index counters.
|
|
FactoryPtr->Vertex_Increment();
|
|
facevertexindex++;
|
|
}
|
|
}
|
|
|
|
// Clean-up.
|
|
delete [] distanceratios;
|
|
}
|
|
|
|
// Add the faces to the Lightscape solve database.
|
|
for (int faceindex = 0; faceindex < GetFaceCount(); faceindex++) {
|
|
|
|
const unsigned facevertexindexcount = 4;
|
|
|
|
LtTFace face;
|
|
int vertexindex, facevertexcount;
|
|
unsigned facevertexindices [facevertexindexcount];
|
|
|
|
ASSERT (faceindex >= 0 && faceindex < GetFaceCount());
|
|
face = GetFaces() [faceindex];
|
|
|
|
// If the face is not a triangle then it must be a quadrilateral. It is safe to assume
|
|
// that these are the only polygon types that Lightscape supports.
|
|
facevertexcount = face.IsTriangle() ? 3 : 4;
|
|
|
|
for (int v = 0; v < facevertexcount; v++) {
|
|
vertexindex = face.GetVert (v);
|
|
ASSERT (vertexindex >= 0 && vertexindex < GetVertexCount());
|
|
facevertexindices [v] = usedvertices.Get_Index (vertexindex);
|
|
}
|
|
|
|
success = LightscapeSolve::Importer()->Add_Patch_Face (FactoryPtr->Patch_Face_Count(), FactoryPtr->Patch_Count(), facevertexcount, facevertexindices);
|
|
if (!success) throw (unmatchedmodeltext);
|
|
|
|
// Advance patch face counter.
|
|
FactoryPtr->Patch_Face_Increment();
|
|
}
|
|
|
|
// Advance the patch index counter.
|
|
FactoryPtr->Patch_Increment();
|
|
|
|
// Return success.
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* VertexUser::VertexUser -- *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 1/05/00 IML : Created. *
|
|
*=============================================================================================*/
|
|
VertexUser::VertexUser (const LsMeshBuilder &meshbuilder)
|
|
{
|
|
LtTFace face;
|
|
unsigned v, vertexindex, facevertexcount;
|
|
|
|
Count = meshbuilder.GetVertexCount();
|
|
UsedVertexIndices = new int [Count];
|
|
ASSERT (UsedVertexIndices != NULL);
|
|
|
|
// Initialize. All vertices unused.
|
|
for (v = 0; v < Count; v++) {
|
|
UsedVertexIndices [v] = UNUSED;
|
|
}
|
|
|
|
// For each face...
|
|
for (int faceindex = 0; faceindex < meshbuilder.GetFaceCount(); faceindex++) {
|
|
|
|
face = meshbuilder.GetFaces() [faceindex];
|
|
|
|
// If the face is not a triangle then it must be a quadrilateral. It is safe to assume
|
|
// that these are the only polygon types that Lightscape supports.
|
|
facevertexcount = face.IsTriangle() ? 3 : 4;
|
|
|
|
// For each vertex in the face, flag that the vertex is referenced by a face.
|
|
for (v = 0; v < facevertexcount; v++) {
|
|
vertexindex = face.GetVert (v);
|
|
ASSERT (vertexindex >= 0 && vertexindex < Count);
|
|
UsedVertexIndices [vertexindex] = USED;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LightscapeMeshSolve::LightscapeMeshSolve -- *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 7/30/99 IML : Created. *
|
|
*=============================================================================================*/
|
|
LightscapeMeshSolve::LightscapeMeshSolve (LightscapeSolve &solve, ChunkClass &trianglechunk, ChunkClass &vertexchunk)
|
|
: Statistics (vertexchunk.Get_Size() / sizeof (W3dVectorStruct), trianglechunk.Get_Size() / sizeof (W3dTriStruct))
|
|
{
|
|
const unsigned verticesperface = 3; // No. of vertices in a triangle.
|
|
|
|
PackingTriangle *triangles;
|
|
W3dTriStruct *w3dfaces;
|
|
W3dVectorStruct *w3dvertices;
|
|
unsigned f, v;
|
|
|
|
// Calculate no. of faces and face-vertices from triangle chunk.
|
|
VertexCount = vertexchunk.Get_Size() / sizeof (W3dVectorStruct);
|
|
FaceCount = trianglechunk.Get_Size() / sizeof (W3dTriStruct);
|
|
FaceVertexCount = FaceCount * verticesperface;
|
|
|
|
// Allocate data tables for this mesh.
|
|
VertexColors = new W3dRGBStruct [VertexCount];
|
|
ASSERT (VertexColors != NULL);
|
|
FaceVertexUVs = new Vector2 [FaceVertexCount];
|
|
ASSERT (FaceVertexUVs != NULL);
|
|
FaceRemapLightmapIndices = new unsigned [FaceCount];
|
|
ASSERT (FaceRemapLightmapIndices != NULL);
|
|
LightmapIndices = new unsigned [FaceCount];
|
|
ASSERT (LightmapIndices != NULL);
|
|
|
|
triangles = new PackingTriangle [FaceCount];
|
|
ASSERT (triangles != NULL);
|
|
|
|
w3dfaces = (W3dTriStruct*) trianglechunk.Get_Data();
|
|
ASSERT (w3dfaces != NULL);
|
|
w3dvertices = (W3dVectorStruct*) vertexchunk.Get_Data();
|
|
ASSERT (w3dvertices != NULL);
|
|
|
|
// For each vertex in the vertex chunk...
|
|
for (unsigned vertexindex = 0; vertexindex < VertexCount; vertexindex++) {
|
|
|
|
const Vector3 zero (0.0f, 0.0f, 0.0f);
|
|
|
|
Vector3 *smoothingnormalptr;
|
|
Vector3 point;
|
|
W3dVectorStruct *w3dvertexptr;
|
|
W3dRGBStruct vertexcolor;
|
|
|
|
// Iterate over the faces and calculate a smoothing normal for this vertex based on
|
|
// the faces that reference this vertex.
|
|
smoothingnormalptr = NULL;
|
|
for (f = 0; f < FaceCount; f++) {
|
|
for (v = 0; v < verticesperface; v++) {
|
|
if (w3dfaces [f].Vindex [v] == vertexindex) {
|
|
|
|
Vector3 facenormal;
|
|
|
|
// Do not consider face normals that are zero (face is degenerate).
|
|
facenormal.Set (w3dfaces [f].Normal.X, w3dfaces [f].Normal.Y, w3dfaces [f].Normal.Z);
|
|
if (facenormal != zero) smoothingnormalptr = &facenormal;
|
|
break;
|
|
}
|
|
}
|
|
if (smoothingnormalptr != NULL) break;
|
|
}
|
|
|
|
w3dvertexptr = w3dvertices + vertexindex;
|
|
point.Set (w3dvertexptr->X, w3dvertexptr->Y, w3dvertexptr->Z);
|
|
|
|
solve.Find_Vertex (point, smoothingnormalptr, vertexcolor, Statistics);
|
|
VertexColors [vertexindex] = vertexcolor;
|
|
}
|
|
|
|
// For each face in the w3d triangle chunk, match it up with a face in the Lightscape solve database.
|
|
for (f = 0; f < FaceCount; f++) {
|
|
|
|
Vector3 points [verticesperface];
|
|
Vector3 facenormal;
|
|
Vector2 uvs [verticesperface];
|
|
PackingTriangle triangle;
|
|
|
|
// Extract points and face normal from vertex chunk.
|
|
for (v = 0; v < verticesperface; v++) {
|
|
|
|
W3dVectorStruct *w3dvertexptr;
|
|
unsigned vindex;
|
|
|
|
vindex = w3dfaces [f].Vindex [v];
|
|
w3dvertexptr = w3dvertices + vindex;
|
|
points [v].Set (w3dvertexptr->X, w3dvertexptr->Y, w3dvertexptr->Z);
|
|
}
|
|
facenormal.Set (w3dfaces [f].Normal.X, w3dfaces [f].Normal.Y, w3dfaces [f].Normal.Z);
|
|
solve.Find_Triangle (points, facenormal, triangles [f], Statistics);
|
|
}
|
|
|
|
if (Statistics.Valid_Lightmap_Solve()) {
|
|
|
|
unsigned lightmapindex, remaplightmapindex;
|
|
bool found;
|
|
|
|
// Submit the triangles for packing.
|
|
for (f = 0; f < FaceCount; f++) {
|
|
solve.Submit_Triangle (triangles [f]);
|
|
}
|
|
|
|
// Now pack.
|
|
solve.Pack();
|
|
|
|
// Extract the lightmap UV's from the triangles.
|
|
v = 0;
|
|
for (f = 0; f < FaceCount; f++) {
|
|
FaceVertexUVs [v + 0] = triangles [f].PackedUVs [0];
|
|
FaceVertexUVs [v + 1] = triangles [f].PackedUVs [1];
|
|
FaceVertexUVs [v + 2] = triangles [f].PackedUVs [2];
|
|
v += verticesperface;
|
|
}
|
|
|
|
// The face-lightmap indices are currently non-zero based, unordered, non-contiguous indices
|
|
// ie. an unknown list of indexes that have been returned from the packing process.
|
|
// The mesh requires face-lightmap indices that are zero-based, ordered, contiguous indices
|
|
// so remap the indices to create face-remaplightmap indices. Also, create a table of 'real'
|
|
// lightmap indices that will be indexed by the face-remaplightmap table.
|
|
remaplightmapindex = 0;
|
|
for (f = 0; f < FaceCount; f++) {
|
|
|
|
int g;
|
|
|
|
lightmapindex = triangles [f].PackedTextureID;
|
|
|
|
// Look backwards thru the face-lightmap table to see if this index has already occured?
|
|
found = false;
|
|
g = ((int) f) - 1;
|
|
while (g >= 0) {
|
|
if (triangles [g].PackedTextureID == lightmapindex) {
|
|
found = true;
|
|
break;
|
|
}
|
|
g--;
|
|
}
|
|
|
|
// Was the index found?
|
|
if (found) {
|
|
FaceRemapLightmapIndices [f] = FaceRemapLightmapIndices [g];
|
|
} else {
|
|
FaceRemapLightmapIndices [f] = remaplightmapindex;
|
|
ASSERT (remaplightmapindex < FaceCount);
|
|
LightmapIndices [remaplightmapindex] = lightmapindex;
|
|
remaplightmapindex++;
|
|
}
|
|
}
|
|
|
|
ASSERT (remaplightmapindex > 0);
|
|
LightmapCount = remaplightmapindex;
|
|
|
|
} else {
|
|
LightmapCount = 0;
|
|
}
|
|
|
|
// Clean-up.
|
|
delete [] triangles;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LightscapeMeshSolve::~LightscapeMeshSolve -- *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 7/30/99 IML : Created. *
|
|
*=============================================================================================*/
|
|
LightscapeMeshSolve::~LightscapeMeshSolve()
|
|
{
|
|
// Clean-up.
|
|
delete [] VertexColors;
|
|
delete [] LightmapIndices;
|
|
delete [] FaceRemapLightmapIndices;
|
|
delete [] FaceVertexUVs;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LightscapeMeshSolve::Vertex_Color -- *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 1/06/00 IML : Created. *
|
|
*=============================================================================================*/
|
|
W3dRGBStruct LightscapeMeshSolve::Vertex_Color (unsigned vertexindex) const
|
|
{
|
|
ASSERT (Statistics.Valid_Vertex_Solve());
|
|
ASSERT (vertexindex < VertexCount);
|
|
return (VertexColors [vertexindex]);
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LightscapeMeshSolve::LightscapeMeshSolve -- *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 7/30/99 IML : Created. *
|
|
*=============================================================================================*/
|
|
const char *LightscapeMeshSolve::Lightmap_Pathname (unsigned lightmapindex) const
|
|
{
|
|
ASSERT (Statistics.Valid_Lightmap_Solve());
|
|
ASSERT (lightmapindex < FaceCount);
|
|
return (LightscapeSolve::Lightmap_Pathname (LightmapIndices [lightmapindex]));
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LightscapeMeshSolve::LightscapeMeshSolve -- *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 7/30/99 IML : Created. *
|
|
*=============================================================================================*/
|
|
unsigned LightscapeMeshSolve::Lightmap_Index (unsigned faceindex) const
|
|
{
|
|
ASSERT (Statistics.Valid_Lightmap_Solve());
|
|
ASSERT (faceindex < FaceCount);
|
|
return (FaceRemapLightmapIndices [faceindex]);
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* LightscapeMeshSolve::LightscapeMeshSolve -- *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 7/30/99 IML : Created. *
|
|
*=============================================================================================*/
|
|
W3dTexCoordStruct LightscapeMeshSolve::Lightmap_UV (unsigned facevertexindex) const
|
|
{
|
|
W3dTexCoordStruct w3duv;
|
|
|
|
ASSERT (Statistics.Valid_Lightmap_Solve());
|
|
ASSERT (facevertexindex < FaceVertexCount);
|
|
|
|
w3duv.U = FaceVertexUVs [facevertexindex].U;
|
|
w3duv.V = FaceVertexUVs [facevertexindex].V;
|
|
return (w3duv);
|
|
}
|