• 12
• 12
• 9
• 10
• 13
• ### Similar Content

• By elect
Hi,
ok, so, we are having problems with our current mirror reflection implementation.
At the moment we are doing it very simple, so for the i-th frame, we calculate the reflection vectors given the viewPoint and some predefined points on the mirror surface (position and normal).
Then, using the least squared algorithm, we find the point that has the minimum distance from all these reflections vectors. This is going to be our virtual viewPoint (with the right orientation).
After that, we render offscreen to a texture by setting the OpenGL camera on the virtual viewPoint.
And finally we use the rendered texture on the mirror surface.
So far this has always been fine, but now we are having some more strong constraints on accuracy.
What are our best options given that:
- we have a dynamic scene, the mirror and parts of the scene can change continuously from frame to frame
- we have about 3k points (with normals) per mirror, calculated offline using some cad program (such as Catia)
- all the mirror are always perfectly spherical (with different radius vertically and horizontally) and they are always convex
- a scene can have up to 10 mirror
- it should be fast enough also for vr (Htc Vive) on fastest gpus (only desktops)

Looking around, some papers talk about calculating some caustic surface derivation offline, but I don't know if this suits my case
Also, another paper, used some acceleration structures to detect the intersection between the reflection vectors and the scene, and then adjust the corresponding texture coordinate. This looks the most accurate but also very heavy from a computational point of view.

Other than that, I couldn't find anything updated/exhaustive around, can you help me?

• Hello all,
I am currently working on a game engine for use with my game development that I would like to be as flexible as possible.  As such the exact requirements for how things should work can't be nailed down to a specific implementation and I am looking for, at least now, a default good average case scenario design.
Here is what I have implemented:
Deferred rendering using OpenGL Arbitrary number of lights and shadow mapping Each rendered object, as defined by a set of geometry, textures, animation data, and a model matrix is rendered with its own draw call Skeletal animations implemented on the GPU.   Model matrix transformation implemented on the GPU Frustum and octree culling for optimization Here are my questions and concerns:
Doing the skeletal animation on the GPU, currently, requires doing the skinning for each object multiple times per frame: once for the initial geometry rendering and once for the shadow map rendering for each light for which it is not culled.  This seems very inefficient.  Is there a way to do skeletal animation on the GPU only once across these render calls? Without doing the model matrix transformation on the CPU, I fail to see how I can easily batch objects with the same textures and shaders in a single draw call without passing a ton of matrix data to the GPU (an array of model matrices then an index for each vertex into that array for transformation purposes?) If I do the matrix transformations on the CPU, It seems I can't really do the skinning on the GPU as the pre-transformed vertexes will wreck havoc with the calculations, so this seems not viable unless I am missing something Overall it seems like simplest solution is to just do all of the vertex manipulation on the CPU and pass the pre-transformed data to the GPU, using vertex shaders that do basically nothing.  This doesn't seem the most efficient use of the graphics hardware, but could potentially reduce the number of draw calls needed.

Really, I am looking for some advice on how to proceed with this, how something like this is typically handled.  Are the multiple draw calls and skinning calculations not a huge deal?  I would LIKE to save as much of the CPU's time per frame so it can be tasked with other things, as to keep CPU resources open to the implementation of the engine.  However, that becomes a moot point if the GPU becomes a bottleneck.

• Hello!
I would like to introduce Diligent Engine, a project that I've been recently working on. Diligent Engine is a light-weight cross-platform abstraction layer between the application and the platform-specific graphics API. Its main goal is to take advantages of the next-generation APIs such as Direct3D12 and Vulkan, but at the same time provide support for older platforms via Direct3D11, OpenGL and OpenGLES. Diligent Engine exposes common front-end for all supported platforms and provides interoperability with underlying native API. Shader source code converter allows shaders authored in HLSL to be translated to GLSL and used on all platforms. Diligent Engine supports integration with Unity and is designed to be used as a graphics subsystem in a standalone game engine, Unity native plugin or any other 3D application. It is distributed under Apache 2.0 license and is free to use. Full source code is available for download on GitHub.
Features:
True cross-platform Exact same client code for all supported platforms and rendering backends No #if defined(_WIN32) ... #elif defined(LINUX) ... #elif defined(ANDROID) ... No #if defined(D3D11) ... #elif defined(D3D12) ... #elif defined(OPENGL) ... Exact same HLSL shaders run on all platforms and all backends Modular design Components are clearly separated logically and physically and can be used as needed Only take what you need for your project (do not want to keep samples and tutorials in your codebase? Simply remove Samples submodule. Only need core functionality? Use only Core submodule) No 15000 lines-of-code files Clear object-based interface No global states Key graphics features: Automatic shader resource binding designed to leverage the next-generation rendering APIs Multithreaded command buffer generation 50,000 draw calls at 300 fps with D3D12 backend Descriptor, memory and resource state management Modern c++ features to make code fast and reliable The following platforms and low-level APIs are currently supported:
Windows Desktop: Direct3D11, Direct3D12, OpenGL Universal Windows: Direct3D11, Direct3D12 Linux: OpenGL Android: OpenGLES MacOS: OpenGL iOS: OpenGLES API Basics
Initialization
The engine can perform initialization of the API or attach to already existing D3D11/D3D12 device or OpenGL/GLES context. For instance, the following code shows how the engine can be initialized in D3D12 mode:
#include "RenderDeviceFactoryD3D12.h" using namespace Diligent; // ...  GetEngineFactoryD3D12Type GetEngineFactoryD3D12 = nullptr; // Load the dll and import GetEngineFactoryD3D12() function LoadGraphicsEngineD3D12(GetEngineFactoryD3D12); auto *pFactoryD3D11 = GetEngineFactoryD3D12(); EngineD3D12Attribs EngD3D12Attribs; EngD3D12Attribs.CPUDescriptorHeapAllocationSize[0] = 1024; EngD3D12Attribs.CPUDescriptorHeapAllocationSize[1] = 32; EngD3D12Attribs.CPUDescriptorHeapAllocationSize[2] = 16; EngD3D12Attribs.CPUDescriptorHeapAllocationSize[3] = 16; EngD3D12Attribs.NumCommandsToFlushCmdList = 64; RefCntAutoPtr<IRenderDevice> pRenderDevice; RefCntAutoPtr<IDeviceContext> pImmediateContext; SwapChainDesc SwapChainDesc; RefCntAutoPtr<ISwapChain> pSwapChain; pFactoryD3D11->CreateDeviceAndContextsD3D12( EngD3D12Attribs, &pRenderDevice, &pImmediateContext, 0 ); pFactoryD3D11->CreateSwapChainD3D12( pRenderDevice, pImmediateContext, SwapChainDesc, hWnd, &pSwapChain ); Creating Resources
Device resources are created by the render device. The two main resource types are buffers, which represent linear memory, and textures, which use memory layouts optimized for fast filtering. To create a buffer, you need to populate BufferDesc structure and call IRenderDevice::CreateBuffer(). The following code creates a uniform (constant) buffer:
BufferDesc BuffDesc; BufferDesc.Name = "Uniform buffer"; BuffDesc.BindFlags = BIND_UNIFORM_BUFFER; BuffDesc.Usage = USAGE_DYNAMIC; BuffDesc.uiSizeInBytes = sizeof(ShaderConstants); BuffDesc.CPUAccessFlags = CPU_ACCESS_WRITE; m_pDevice->CreateBuffer( BuffDesc, BufferData(), &m_pConstantBuffer ); Similar, to create a texture, populate TextureDesc structure and call IRenderDevice::CreateTexture() as in the following example:
TextureDesc TexDesc; TexDesc.Name = "My texture 2D"; TexDesc.Type = TEXTURE_TYPE_2D; TexDesc.Width = 1024; TexDesc.Height = 1024; TexDesc.Format = TEX_FORMAT_RGBA8_UNORM; TexDesc.Usage = USAGE_DEFAULT; TexDesc.BindFlags = BIND_SHADER_RESOURCE | BIND_RENDER_TARGET | BIND_UNORDERED_ACCESS; TexDesc.Name = "Sample 2D Texture"; m_pRenderDevice->CreateTexture( TexDesc, TextureData(), &m_pTestTex ); Initializing Pipeline State
Diligent Engine follows Direct3D12 style to configure the graphics/compute pipeline. One big Pipelines State Object (PSO) encompasses all required states (all shader stages, input layout description, depth stencil, rasterizer and blend state descriptions etc.)
To create a shader, populate ShaderCreationAttribs structure. An important member is ShaderCreationAttribs::SourceLanguage. The following are valid values for this member:
SHADER_SOURCE_LANGUAGE_DEFAULT  - The shader source format matches the underlying graphics API: HLSL for D3D11 or D3D12 mode, and GLSL for OpenGL and OpenGLES modes. SHADER_SOURCE_LANGUAGE_HLSL  - The shader source is in HLSL. For OpenGL and OpenGLES modes, the source code will be converted to GLSL. See shader converter for details. SHADER_SOURCE_LANGUAGE_GLSL  - The shader source is in GLSL. There is currently no GLSL to HLSL converter. To allow grouping of resources based on the frequency of expected change, Diligent Engine introduces classification of shader variables:
Static variables (SHADER_VARIABLE_TYPE_STATIC) are variables that are expected to be set only once. They may not be changed once a resource is bound to the variable. Such variables are intended to hold global constants such as camera attributes or global light attributes constant buffers. Mutable variables (SHADER_VARIABLE_TYPE_MUTABLE) define resources that are expected to change on a per-material frequency. Examples may include diffuse textures, normal maps etc. Dynamic variables (SHADER_VARIABLE_TYPE_DYNAMIC) are expected to change frequently and randomly. This post describes the resource binding model in Diligent Engine.
The following is an example of shader initialization:
To create a pipeline state object, define instance of PipelineStateDesc structure. The structure defines the pipeline specifics such as if the pipeline is a compute pipeline, number and format of render targets as well as depth-stencil format:
// This is a graphics pipeline PSODesc.IsComputePipeline = false; PSODesc.GraphicsPipeline.NumRenderTargets = 1; PSODesc.GraphicsPipeline.RTVFormats[0] = TEX_FORMAT_RGBA8_UNORM_SRGB; PSODesc.GraphicsPipeline.DSVFormat = TEX_FORMAT_D32_FLOAT; The structure also defines depth-stencil, rasterizer, blend state, input layout and other parameters. For instance, rasterizer state can be defined as in the code snippet below:
// Init rasterizer state RasterizerStateDesc &RasterizerDesc = PSODesc.GraphicsPipeline.RasterizerDesc; RasterizerDesc.FillMode = FILL_MODE_SOLID; RasterizerDesc.CullMode = CULL_MODE_NONE; RasterizerDesc.FrontCounterClockwise = True; RasterizerDesc.ScissorEnable = True; //RSDesc.MultisampleEnable = false; // do not allow msaa (fonts would be degraded) RasterizerDesc.AntialiasedLineEnable = False; When all fields are populated, call IRenderDevice::CreatePipelineState() to create the PSO:
Shader resource binding in Diligent Engine is based on grouping variables in 3 different groups (static, mutable and dynamic). Static variables are variables that are expected to be set only once. They may not be changed once a resource is bound to the variable. Such variables are intended to hold global constants such as camera attributes or global light attributes constant buffers. They are bound directly to the shader object:

m_pPSO->CreateShaderResourceBinding(&m_pSRB); Dynamic and mutable resources are then bound through SRB object:
m_pSRB->GetVariable(SHADER_TYPE_VERTEX, "tex2DDiffuse")->Set(pDiffuseTexSRV); m_pSRB->GetVariable(SHADER_TYPE_VERTEX, "cbRandomAttribs")->Set(pRandomAttrsCB); The difference between mutable and dynamic resources is that mutable ones can only be set once for every instance of a shader resource binding. Dynamic resources can be set multiple times. It is important to properly set the variable type as this may affect performance. Static variables are generally most efficient, followed by mutable. Dynamic variables are most expensive from performance point of view. This post explains shader resource binding in more details.
Setting the Pipeline State and Invoking Draw Command
Before any draw command can be invoked, all required vertex and index buffers as well as the pipeline state should be bound to the device context:
// Clear render target const float zero[4] = {0, 0, 0, 0}; m_pContext->ClearRenderTarget(nullptr, zero); // Set vertex and index buffers IBuffer *buffer[] = {m_pVertexBuffer}; Uint32 offsets[] = {0}; Uint32 strides[] = {sizeof(MyVertex)}; m_pContext->SetVertexBuffers(0, 1, buffer, strides, offsets, SET_VERTEX_BUFFERS_FLAG_RESET); m_pContext->SetIndexBuffer(m_pIndexBuffer, 0); m_pContext->SetPipelineState(m_pPSO); Also, all shader resources must be committed to the device context:
m_pContext->CommitShaderResources(m_pSRB, COMMIT_SHADER_RESOURCES_FLAG_TRANSITION_RESOURCES); When all required states and resources are bound, IDeviceContext::Draw() can be used to execute draw command or IDeviceContext::DispatchCompute() can be used to execute compute command. Note that for a draw command, graphics pipeline must be bound, and for dispatch command, compute pipeline must be bound. Draw() takes DrawAttribs structure as an argument. The structure members define all attributes required to perform the command (primitive topology, number of vertices or indices, if draw call is indexed or not, if draw call is instanced or not, if draw call is indirect or not, etc.). For example:
DrawAttribs attrs; attrs.IsIndexed = true; attrs.IndexType = VT_UINT16; attrs.NumIndices = 36; attrs.Topology = PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; pContext->Draw(attrs); Tutorials and Samples
The GitHub repository contains a number of tutorials and sample applications that demonstrate the API usage.

AntTweakBar sample demonstrates how to use AntTweakBar library to create simple user interface.

Atmospheric scattering sample is a more advanced example. It demonstrates how Diligent Engine can be used to implement various rendering tasks: loading textures from files, using complex shaders, rendering to textures, using compute shaders and unordered access views, etc.

The repository includes Asteroids performance benchmark based on this demo developed by Intel. It renders 50,000 unique textured asteroids and lets compare performance of D3D11 and D3D12 implementations. Every asteroid is a combination of one of 1000 unique meshes and one of 10 unique textures.

Integration with Unity
Diligent Engine supports integration with Unity through Unity low-level native plugin interface. The engine relies on Native API Interoperability to attach to the graphics API initialized by Unity. After Diligent Engine device and context are created, they can be used us usual to create resources and issue rendering commands. GhostCubePlugin shows an example how Diligent Engine can be used to render a ghost cube only visible as a reflection in a mirror.

• By Yxjmir
I'm trying to load data from a .gltf file into a struct to use to load a .bin file. I don't think there is a problem with how the vertex positions are loaded, but with the indices. This is what I get when drawing with glDrawArrays(GL_LINES, ...):

Also, using glDrawElements gives a similar result. Since it looks like its drawing triangles using the wrong vertices for each face, I'm assuming it needs an index buffer/element buffer. (I'm not sure why there is a line going through part of it, it doesn't look like it belongs to a side, re-exported it without texture coordinates checked, and its not there)
I'm using jsoncpp to load the GLTF file, its format is based on JSON. Here is the gltf struct I'm using, and how I parse the file:
glBindVertexArray(g_pGame->m_VAO);
glDrawElements(GL_LINES, g_pGame->m_indices.size(), GL_UNSIGNED_BYTE, (void*)0); // Only shows with GL_UNSIGNED_BYTE
glDrawArrays(GL_LINES, 0, g_pGame->m_vertexCount);
So, I'm asking what type should I use for the indices? it doesn't seem to be unsigned short, which is what I selected with the Khronos Group Exporter for blender. Also, am I reading part or all of the .bin file wrong?
Test.gltf
Test.bin

• That means how do I use base DirectX or OpenGL api's to make a physics based destruction simulation?
Will it be just smart rendering or something else is required?

# OpenGL 3ds loader problem (crashes)

This topic is 3962 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

im having some trouble loading 3ds models... when i try to load a model I get an error and the program crashes... the loader when i try to load very simple models ive created quickly.. something like a textured box... any ideas what might be causing it? there error is something like "Debug assertion" and "Expression: _CrtCheckMemory" im using the following loader code..


#ifndef MODEL_3DS_H
#define MODEL_3DS_H

// I decided to use my CIMAGE class b/c adding all of its functions
// Would have greatly bloated the model class's code
// Just replace this with your favorite texture class
#include "BaseCode/Image.h"
#include "BaseCode/Math/math.h"
#include <stdio.h>

class Model_3DS
{
public:
// A VERY simple vector struct
// I could have included a complex class but I wanted the model class to stand alone
struct Vector {
float x;
float y;
float z;
};

// Vertex struct to make code easier to read in places
struct Vertex {
float x;
float y;
float z;
};

// Color struct holds the diffuse color of the material
struct Color4i {
unsigned char r;
unsigned char g;
unsigned char b;
unsigned char a;
};

// Holds the material info
// TODO: add color support for non textured polys
struct Material {
char name[80];	// The material's name
CIMAGE tex;	// The texture (this is the only outside reference in this class)
bool textured;	// whether or not it is textured
Color4i color;
};

// Every chunk in the 3ds file starts with this struct
unsigned short id;	// The chunk's id
unsigned long  len;	// The lenght of the chunk
};

// I sort the mesh by material so that I won't have to switch textures a great deal
struct MaterialFaces {
unsigned short *subFaces;	// Index to our vertex array of all the faces that use this material
int numSubFaces;			// The number of faces
int MatIndex;				// An index to our materials
};

// The 3ds file can be made up of several objects
struct Object {
char name[80];				// The object name
float *Vertexes;			// The array of vertices
float *Normals;				// The array of the normals for the vertices
float *Tangents;				// The array of the normals for the vertices
float *TexCoords;			// The array of texture coordinates for the vertices
unsigned short *Faces;		// The array of face indices
int numFaces;				// The number of faces
int numMatFaces;			// The number of differnet material faces
int numVerts;				// The number of vertices
int numTexCoords;			// The number of vertices
bool textured;				// True: the object has textures
MaterialFaces *MatFaces;	// The faces are divided by materials
Vector pos;					// The position to move the object to
Vector rot;					// The angles to rotate the object
};

char *modelname;		// The name of the model
char *path;				// The path of the model
int numObjects;			// Total number of objects in the model
int numMaterials;		// Total number of materials in the model
int totalVerts;			// Total number of vertices in the model
int totalFaces;			// Total number of faces in the model
bool shownormals;		// True: show the normals
Material *Materials;	// The array of materials
Object *Objects;		// The array of objects in the model
Vector pos;				// The position to move the model to
Vector rot;				// The angles to rotate the model
float scale;			// The size you want the model scaled to
bool lit;				// True: the model is lit
bool visible;			// True: the model gets rendered
void Draw();			// Draws the model
FILE *bin3ds;			// The binary 3ds file
Model_3DS();			// Constructor
virtual ~Model_3DS();	// Destructor

private:
void IntColorChunkProcessor(long length, long findex, int matindex);
void FloatColorChunkProcessor(long length, long findex, int matindex);
// Processes the Main Chunk that all the other chunks exist is
void MainChunkProcessor(long length, long findex);
// Processes the model's info
void EditChunkProcessor(long length, long findex);

// Processes the model's materials
void MaterialChunkProcessor(long length, long findex, int matindex);
// Processes the names of the materials
void MaterialNameChunkProcessor(long length, long findex, int matindex);
// Processes the material's diffuse color
void DiffuseColorChunkProcessor(long length, long findex, int matindex);
// Processes the material's texture maps
void TextureMapChunkProcessor(long length, long findex, int matindex);
// Processes the names of the textures and load the textures
void MapNameChunkProcessor(long length, long findex, int matindex);

// Processes the model's geometry
void ObjectChunkProcessor(long length, long findex, int objindex);
// Processes the triangles of the model
void TriangularMeshChunkProcessor(long length, long findex, int objindex);
// Processes the vertices of the model and loads them
void VertexListChunkProcessor(long length, long findex, int objindex);
// Processes the texture cordiantes of the vertices and loads them
void TexCoordsChunkProcessor(long length, long findex, int objindex);
// Processes the faces of the model and loads the faces
void FacesDescriptionChunkProcessor(long length, long findex, int objindex);
// Processes the materials of the faces and splits them up by material
void FacesMaterialsListChunkProcessor(long length, long findex, int objindex, int subfacesindex);

// Calculates the normals of the vertices by averaging
// the normals of the faces that use that vertex
void CalculateNormals();
};

#endif MODEL_3DS_H

// This is used to generate a warning from the compiler
#define _QUOTE(x) # x
#define QUOTE(x) _QUOTE(x)
#define __FILE__LINE__ __FILE__ "(" QUOTE(__LINE__) ") : "
#define warn( x )  message( __FILE__LINE__ #x "\n" )

// You need to uncomment this if you are using MFC
#pragma warn( You need to uncomment this if you are using MFC )
//#include "stdafx.h"

#include "Model_3DS.h"

#include <math.h>			// Header file for the math library
#include <gl\gl.h>			// Header file for the OpenGL32 library

// The chunk's id numbers
#define MAIN3DS				0x4D4D
#define MAIN_VERS			0x0002
#define EDIT3DS			0x3D3D
#define MESH_VERS			0x3D3E
#define OBJECT			0x4000
#define TRIG_MESH		0x4100
#define VERT_LIST		0x4110
#define FACE_DESC		0x4120
#define FACE_MAT		0x4130
#define TEX_VERTS		0x4140
#define SMOOTH_GROUP	0x4150
#define LOCAL_COORDS	0x4160
#define MATERIAL			0xAFFF
#define MAT_NAME			0xA000
#define MAT_AMBIENT		0xA010
#define MAT_DIFFUSE		0xA020
#define MAT_SPECULAR		0xA030
#define SHINY_PERC		0xA040
#define SHINY_STR_PERC	0xA041
#define TRANS_PERC		0xA050
#define TRANS_FOFF_PERC	0xA052
#define REF_BLUR_PERC	0xA053
#define RENDER_TYPE		0xA100
#define SELF_ILLUM		0xA084
#define MAT_SELF_ILPCT	0xA08A
#define WIRE_THICKNESS	0xA087
#define MAT_TEXMAP		0xA200
#define MAT_MAPNAME		0xA300
#define ONE_UNIT			0x0100
#define KEYF3DS			0xB000
#define FRAMES			0xB008
#define MESH_INFO			0xB002
#define HIER_POS			0xB030
#define HIER_FATHER		0xB010
#define PIVOT_PT			0xB013
#define TRACK00			0xB020
#define TRACK01			0xB021
#define TRACK02			0xB022
#define	COLOR_RGB			0x0010
#define COLOR_TRU			0x0011
#define COLOR_TRUG			0x0012
#define COLOR_RGBG			0x0013
#define PERC_INT			0x0030
#define PERC_FLOAT			0x0031

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

Model_3DS::Model_3DS()
{
// Initialization

// Don't show the normals by default
shownormals = false;

// The model is lit by default
lit = true;

// The model is visible by default
visible = true;

// Set up the default position
pos.x = 0.0f;
pos.y = 0.0f;
pos.z = 0.0f;
// Set up the default rotation
rot.x = 0.0f;
rot.y = 0.0f;
rot.z = 0.0f;

// Set up the path
path = new char[80];
sprintf(path, "");

// Zero out our counters for MFC
numObjects = 0;
numMaterials = 0;

// Set the scale to one
scale = 1.0f;
}

Model_3DS::~Model_3DS()
{

}

{
// holds the main chunk header

// strip "'s
if (strstr(name, "\""))
name = strtok(name, "\"");

// Find the path
if (strstr(name, "/") || strstr(name, "\\"))
{
// Holds the name of the model minus the path
char *temp;

// Find the name without the path
if (strstr(name, "/"))
temp = strrchr(name, '/');
else
temp = strrchr(name, '\\');

// Allocate space for the path
path = new char[strlen(name)-strlen(temp)+1];

// Get a pointer to the end of the path and name
char *src = name + strlen(name) - 1;

// Back up until a \ or the start
while (src != path && !((*(src-1)) == '\\' || (*(src-1)) == '/'))
src--;

// Copy the path into path
memcpy (path, name, src-name);
path[src-name] = 0;
}

bin3ds = fopen(name,"rb");

// Make sure we are at the beginning
fseek(bin3ds, 0, SEEK_SET);

// Start Processing
MainChunkProcessor(main.len, ftell(bin3ds));

// Don't need the file anymore so close it
fclose(bin3ds);

// Calculate the vertex normals
CalculateNormals();

// For future reference
modelname = name;

// Find the total number of faces and vertices
totalFaces = 0;
totalVerts = 0;

for (int i = 0; i < numObjects; i ++)
{
totalFaces += Objects.numFaces/3;
totalVerts += Objects.numVerts;
}

// If the object doesn't have any texcoords generate some
for (int k = 0; k < numObjects; k++)
{
if (Objects[k].numTexCoords == 0)
{
// Set the number of texture coords
Objects[k].numTexCoords = Objects[k].numVerts;

// Allocate an array to hold the texture coordinates
Objects[k].TexCoords = new GLfloat[Objects[k].numTexCoords * 2];

// Make some texture coords
for (int m = 0; m < Objects[k].numTexCoords; m++)
{
Objects[k].TexCoords[2*m] = Objects[k].Vertexes[3*m];
Objects[k].TexCoords[2*m+1] = Objects[k].Vertexes[3*m+1];
}
}
}

// Let's build simple colored textures for the materials w/o a texture
for (int j = 0; j < numMaterials; j++)
{
if (Materials[j].textured == false)
{
unsigned char r = Materials[j].color.r;
unsigned char g = Materials[j].color.g;
unsigned char b = Materials[j].color.b;
//Materials[j].tex.BuildColorTexture(r, g, b);
Materials[j].textured = true;
}
}
}
void Model_3DS::Draw()
{
if (visible)
{
glPushMatrix();

// Move the model
glTranslatef(pos.x, pos.y, pos.z);

// Rotate the model
glRotatef(rot.x, 1.0f, 0.0f, 0.0f);
glRotatef(rot.y, 0.0f, 1.0f, 0.0f);
glRotatef(rot.z, 0.0f, 0.0f, 1.0f);

glScalef(scale, scale, scale);

// Loop through the objects
for (int i = 0; i < numObjects; i++)
{
// Enable texture coordiantes, normals, and vertices arrays
if (Objects.textured)
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
if (lit)
{
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
}

glEnableClientState(GL_VERTEX_ARRAY);

// Point them to the objects arrays
if (Objects.textured)
glTexCoordPointer(2, GL_FLOAT, 0, Objects.TexCoords);
if (lit)
{
glNormalPointer(GL_FLOAT, 0, Objects.Normals);
glColorPointer(3, GL_FLOAT, 0, Objects.Tangents);
}

glVertexPointer(3, GL_FLOAT, 0, Objects.Vertexes);

// Loop through the faces as sorted by material and draw them
for (int j = 0; j < Objects.numMatFaces; j ++)
{
// Use the material's texture
Materials[Objects.MatFaces[j].MatIndex].tex.Bind();

glPushMatrix();

// Move the model
glTranslatef(Objects.pos.x, Objects.pos.y, Objects.pos.z);

// Rotate the model
//glRotatef(Objects.rot.x, 1.0f, 0.0f, 0.0f);
//glRotatef(Objects.rot.y, 0.0f, 1.0f, 0.0f);
//glRotatef(Objects.rot.z, 0.0f, 0.0f, 1.0f);

glRotatef(Objects.rot.z, 0.0f, 0.0f, 1.0f);
glRotatef(Objects.rot.y, 0.0f, 1.0f, 0.0f);
glRotatef(Objects.rot.x, 1.0f, 0.0f, 0.0f);

// Draw the faces using an index to the vertex array
glDrawElements(GL_TRIANGLES, Objects.MatFaces[j].numSubFaces, GL_UNSIGNED_SHORT, Objects.MatFaces[j].subFaces);

glPopMatrix();
}

// Show the normals?
if (shownormals)
{
// Loop through the vertices and normals and draw the normal
for (int k = 0; k < Objects.numVerts * 2; k += 3)
{
// Disable texturing
glDisable(GL_TEXTURE_2D);
// Disbale lighting if the model is lit
if (lit)
glDisable(GL_LIGHTING);
// Draw the normals blue
glColor3f(0.0f, 0.0f, 1.0f);

// Draw a line between the vertex and the end of the normal
glBegin(GL_LINES);
glVertex3f(Objects.Vertexes[k], Objects.Vertexes[k+1], Objects.Vertexes[k+2]);
glVertex3f(Objects.Vertexes[k]+Objects.Normals[k]*3.0, Objects.Vertexes[k+1]+Objects.Normals[k+1]*3.0, Objects.Vertexes[k+2]+Objects.Normals[k+2]*3.0);
glEnd();

// Reset the color to white
glColor3f(1.0f, 1.0f, 1.0f);
// If the model is lit then renable lighting
if (lit)
glEnable(GL_LIGHTING);
}

for (int k = 0; k < Objects.numVerts * 2; k += 3)
{
// Disable texturing
glDisable(GL_TEXTURE_2D);
// Disbale lighting if the model is lit
if (lit)
glDisable(GL_LIGHTING);
// Draw the normals blue
glColor3f(0.0f, 0.0f, 1.0f);

// Draw a line between the vertex and the end of the normal
glBegin(GL_LINES);
glVertex3f(Objects.Vertexes[k],Objects.Vertexes[k+1], Objects.Vertexes[k+2]);
glVertex3f(Objects.Vertexes[k]+Objects.Tangents[k]*2.0, Objects.Vertexes[k+1]+Objects.Tangents[k+1]*2.0, Objects.Vertexes[k+2]+Objects.Tangents[k+2]*2.0);
glEnd();

// Reset the color to white
glColor3f(1.0f, 1.0f, 1.0f);
// If the model is lit then renable lighting
if (lit)
glEnable(GL_LIGHTING);
}

for (int k = 0; k < Objects.numVerts * 2; k += 3)
{
// Disable texturing
glDisable(GL_TEXTURE_2D);
// Disbale lighting if the model is lit
if (lit)
glDisable(GL_LIGHTING);
// Draw the normals blue
glColor3f(0.0f, 0.0f, 1.0f);

vec3 Normal = vec3(Objects.Normals[k], Objects.Normals[k+1], Objects.Normals[k+2]);
vec3 Tangent = vec3(Objects.Tangents[k], Objects.Tangents[k+1], Objects.Tangents[k+2]);

vec3 BiNormal = cross(Normal,Tangent);
// Draw a line between the vertex and the end of the normal
glBegin(GL_LINES);
glVertex3f(Objects.Vertexes[k],Objects.Vertexes[k+1], Objects.Vertexes[k+2]);
glVertex3f(Objects.Vertexes[k]+BiNormal.x, Objects.Vertexes[k+1]+BiNormal.y, Objects.Vertexes[k+2]+BiNormal.z);
glEnd();

// Reset the color to white
glColor3f(1.0f, 1.0f, 1.0f);
// If the model is lit then renable lighting
if (lit)
glEnable(GL_LIGHTING);
}
}
}

glPopMatrix();
}
}

void Model_3DS::CalculateNormals()
{
// Let's build some normals
for (int i = 0; i < numObjects; i++)
{
for (int g = 0; g < Objects.numVerts; g++)
{
vec3 Pos1 = vec3(Objects.Vertexes[g*3],
Objects.Vertexes[g*3+1],
Objects.Vertexes[g*3+2]);

for (int i2 = 0; i2 < numObjects; i2++)
{
for (int g2 = 0; g2 < Objects[i2].numVerts; g2++)
{
vec3 Pos2 = vec3(Objects[i2].Vertexes[g2*3],
Objects[i2].Vertexes[g2*3+1],
Objects[i2].Vertexes[g2*3+2]);

if (Pos1 == Pos2)
{
Objects.Normals[g*3] += Objects[i2].Normals[g2*3];
Objects.Normals[g*3+1] += Objects[i2].Normals[g2*3+1];
Objects.Normals[g*3+2] += Objects[i2].Normals[g2*3+2];

Objects[i2].Normals[g2*3] = Objects.Normals[g*3];
Objects[i2].Normals[g2*3+1] = Objects.Normals[g*3+1];
Objects[i2].Normals[g2*3+2] = Objects.Normals[g*3+2];

Objects.Tangents[g*3] += Objects[i2].Tangents[g2*3];
Objects.Tangents[g*3+1] += Objects[i2].Tangents[g2*3+1];
Objects.Tangents[g*3+2] += Objects[i2].Tangents[g2*3+2];

Objects[i2].Tangents[g2*3] = Objects.Tangents[g*3];
Objects[i2].Tangents[g2*3+1] = Objects.Tangents[g*3+1];
Objects[i2].Tangents[g2*3+2] = Objects.Tangents[g*3+2];
}
}
}

}
}

for (int i = 0; i < numObjects; i++)
{
for (int g = 0; g < Objects.numVerts; g++)
{
// Reduce each vert's normal to unit
float length;
Vector unit;

unit.x = Objects.Normals[g*3];
unit.y = Objects.Normals[g*3+1];
unit.z = Objects.Normals[g*3+2];

length = (float)sqrt((unit.x*unit.x) + (unit.y*unit.y) + (unit.z*unit.z));

if (length == 0.0f)
length = 1.0f;

unit.x /= length;
unit.y /= length;
unit.z /= length;

Objects.Normals[g*3]   = unit.x;
Objects.Normals[g*3+1] = unit.y;
Objects.Normals[g*3+2] = unit.z;

unit.x = Objects.Tangents[g*3];
unit.y = Objects.Tangents[g*3+1];
unit.z = Objects.Tangents[g*3+2];

length = (float)sqrt((unit.x*unit.x) + (unit.y*unit.y) + (unit.z*unit.z));

if (length == 0.0f)
length = 1.0f;

unit.x /= length;
unit.y /= length;
unit.z /= length;

Objects.Tangents[g*3]   = unit.x;
Objects.Tangents[g*3+1] = unit.y;
Objects.Tangents[g*3+2] = unit.z;
}
}
}

void Model_3DS::MainChunkProcessor(long length, long findex)
{

// move the file pointer to the beginning of the main
// chunk's data findex + the size of the header
fseek(bin3ds, findex, SEEK_SET);

while (ftell(bin3ds) < (findex + length - 6))
{

switch (h.id)
{
// This is the mesh information like vertices, faces, and materials
case EDIT3DS	:
EditChunkProcessor(h.len, ftell(bin3ds));
break;
// I left this in case anyone gets very ambitious
case KEYF3DS	:
//KeyFrameChunkProcessor(h.len, ftell(bin3ds));
break;
default			:
break;
}

fseek(bin3ds, (h.len - 6), SEEK_CUR);
}

// move the file pointer back to where we got it so
// that the ProcessChunk() which we interrupted will read
// from the right place
fseek(bin3ds, findex, SEEK_SET);
}

void Model_3DS::EditChunkProcessor(long length, long findex)
{

// move the file pointer to the beginning of the main
// chunk's data findex + the size of the header
fseek(bin3ds, findex, SEEK_SET);

// First count the number of Objects and Materials
while (ftell(bin3ds) < (findex + length - 6))
{

switch (h.id)
{
case OBJECT	:
numObjects++;
break;
case MATERIAL	:
numMaterials++;
break;
default			:
break;
}

fseek(bin3ds, (h.len - 6), SEEK_CUR);
}

if (numMaterials > 0)
{
Materials = new Material[numMaterials];

// Material is set to untextured until we find otherwise
for (int d = 0; d < numMaterials; d++)
Materials[d].textured = false;

fseek(bin3ds, findex, SEEK_SET);

int i = 0;

while (ftell(bin3ds) < (findex + length - 6))
{

switch (h.id)
{
case MATERIAL	:
MaterialChunkProcessor(h.len, ftell(bin3ds), i);
i++;
break;
default			:
break;
}

fseek(bin3ds, (h.len - 6), SEEK_CUR);
}
}

// Load the Objects (individual meshes in the whole model)
if (numObjects > 0)
{
Objects = new Object[numObjects];

// Set the textured variable to false until we find a texture
for (int k = 0; k < numObjects; k++)
Objects[k].textured = false;

// Zero the objects position and rotation
for (int m = 0; m < numObjects; m++)
{
Objects[m].pos.x = 0.0f;
Objects[m].pos.y = 0.0f;
Objects[m].pos.z = 0.0f;

Objects[m].rot.x = 0.0f;
Objects[m].rot.y = 0.0f;
Objects[m].rot.z = 0.0f;
}

// Zero out the number of texture coords
for (int n = 0; n < numObjects; n++)
Objects[n].numTexCoords = 0;

fseek(bin3ds, findex, SEEK_SET);

int j = 0;

while (ftell(bin3ds) < (findex + length - 6))
{

switch (h.id)
{
case OBJECT	:
ObjectChunkProcessor(h.len, ftell(bin3ds), j);
j++;
break;
default			:
break;
}

fseek(bin3ds, (h.len - 6), SEEK_CUR);
}
}

// move the file pointer back to where we got it so
// that the ProcessChunk() which we interrupted will read
// from the right place
fseek(bin3ds, findex, SEEK_SET);
}

void Model_3DS::MaterialChunkProcessor(long length, long findex, int matindex)
{

// move the file pointer to the beginning of the main
// chunk's data findex + the size of the header
fseek(bin3ds, findex, SEEK_SET);

while (ftell(bin3ds) < (findex + length - 6))
{

switch (h.id)
{
case MAT_NAME	:
MaterialNameChunkProcessor(h.len, ftell(bin3ds), matindex);
break;
case MAT_AMBIENT	:
//ColorChunkProcessor(h.len, ftell(bin3ds));
break;
case MAT_DIFFUSE	:
DiffuseColorChunkProcessor(h.len, ftell(bin3ds), matindex);
break;
case MAT_SPECULAR	:
//ColorChunkProcessor(h.len, ftell(bin3ds));
case MAT_TEXMAP	:
// Finds the names of the textures of the material and loads them
TextureMapChunkProcessor(h.len, ftell(bin3ds), matindex);
break;
default			:
break;
}

fseek(bin3ds, (h.len - 6), SEEK_CUR);
}

// move the file pointer back to where we got it so
// that the ProcessChunk() which we interrupted will read
// from the right place
fseek(bin3ds, findex, SEEK_SET);
}

void Model_3DS::MaterialNameChunkProcessor(long length, long findex, int matindex)
{
// move the file pointer to the beginning of the main
// chunk's data findex + the size of the header
fseek(bin3ds, findex, SEEK_SET);

for (int i = 0; i < 80; i++)
{
Materials[matindex].name = fgetc(bin3ds);
if (Materials[matindex].name == 0)
{
Materials[matindex].name = NULL;
break;
}
}

// move the file pointer back to where we got it so
// that the ProcessChunk() which we interrupted will read
// from the right place
fseek(bin3ds, findex, SEEK_SET);
}

void Model_3DS::DiffuseColorChunkProcessor(long length, long findex, int matindex)
{

// move the file pointer to the beginning of the main
// chunk's data findex + the size of the header
fseek(bin3ds, findex, SEEK_SET);

while (ftell(bin3ds) < (findex + length - 6))
{

// Determine the format of the color and load it
switch (h.id)
{
case COLOR_RGB	:
// A rgb float color chunk
FloatColorChunkProcessor(h.len, ftell(bin3ds), matindex);
break;
case COLOR_TRU	:
// A rgb int color chunk
IntColorChunkProcessor(h.len, ftell(bin3ds), matindex);
break;
case COLOR_RGBG	:
// A rgb gamma corrected float color chunk
FloatColorChunkProcessor(h.len, ftell(bin3ds), matindex);
break;
case COLOR_TRUG	:
// A rgb gamma corrected int color chunk
IntColorChunkProcessor(h.len, ftell(bin3ds), matindex);
break;
default			:
break;
}

fseek(bin3ds, (h.len - 6), SEEK_CUR);
}

// move the file pointer back to where we got it so
// that the ProcessChunk() which we interrupted will read
// from the right place
fseek(bin3ds, findex, SEEK_SET);
}

void Model_3DS::FloatColorChunkProcessor(long length, long findex, int matindex)
{
float r;
float g;
float b;

// move the file pointer to the beginning of the main
// chunk's data findex + the size of the header
fseek(bin3ds, findex, SEEK_SET);

Materials[matindex].color.r = (unsigned char)(r*255.0f);
Materials[matindex].color.g = (unsigned char)(r*255.0f);
Materials[matindex].color.b = (unsigned char)(r*255.0f);
Materials[matindex].color.a = 255;

// move the file pointer back to where we got it so
// that the ProcessChunk() which we interrupted will read
// from the right place
fseek(bin3ds, findex, SEEK_SET);
}

void Model_3DS::IntColorChunkProcessor(long length, long findex, int matindex)
{
unsigned char r;
unsigned char g;
unsigned char b;

// move the file pointer to the beginning of the main
// chunk's data findex + the size of the header
fseek(bin3ds, findex, SEEK_SET);

Materials[matindex].color.r = r;
Materials[matindex].color.g = g;
Materials[matindex].color.b = b;
Materials[matindex].color.a = 255;

// move the file pointer back to where we got it so
// that the ProcessChunk() which we interrupted will read
// from the right place
fseek(bin3ds, findex, SEEK_SET);
}

void Model_3DS::TextureMapChunkProcessor(long length, long findex, int matindex)
{

// move the file pointer to the beginning of the main
// chunk's data findex + the size of the header
fseek(bin3ds, findex, SEEK_SET);

while (ftell(bin3ds) < (findex + length - 6))
{

switch (h.id)
{
case MAT_MAPNAME:
// Read the name of texture in the Diffuse Color map
MapNameChunkProcessor(h.len, ftell(bin3ds), matindex);
break;
default			:
break;
}

fseek(bin3ds, (h.len - 6), SEEK_CUR);
}

// move the file pointer back to where we got it so
// that the ProcessChunk() which we interrupted will read
// from the right place
fseek(bin3ds, findex, SEEK_SET);
}

void Model_3DS::MapNameChunkProcessor(long length, long findex, int matindex)
{
char name[80];

// move the file pointer to the beginning of the main
// chunk's data findex + the size of the header
fseek(bin3ds, findex, SEEK_SET);

// Read the name of the texture
for (int i = 0; i < 80; i++)
{
name = fgetc(bin3ds);
if (name == 0)
{
name = NULL;
break;
}
}

// Load the name and indicate that the material has a texture
char fullname[80];
sprintf(fullname, "%s%s", path, name);
Materials[matindex].textured = true;

// move the file pointer back to where we got it so
// that the ProcessChunk() which we interrupted will read
// from the right place
fseek(bin3ds, findex, SEEK_SET);
}

void Model_3DS::ObjectChunkProcessor(long length, long findex, int objindex)
{

// move the file pointer to the beginning of the main
// chunk's data findex + the size of the header
fseek(bin3ds, findex, SEEK_SET);

for (int i = 0; i < 80; i++)
{
Objects[objindex].name = fgetc(bin3ds);
if (Objects[objindex].name == 0)
{
Objects[objindex].name = NULL;
break;
}
}

while (ftell(bin3ds) < (findex + length - 6))
{

switch (h.id)
{
case TRIG_MESH	:
// Process the triangles of the object
TriangularMeshChunkProcessor(h.len, ftell(bin3ds), objindex);
break;
default			:
break;
}

fseek(bin3ds, (h.len - 6), SEEK_CUR);
}

// move the file pointer back to where we got it so
// that the ProcessChunk() which we interrupted will read
// from the right place
fseek(bin3ds, findex, SEEK_SET);
}

void Model_3DS::TriangularMeshChunkProcessor(long length, long findex, int objindex)
{

// move the file pointer to the beginning of the main
// chunk's data findex + the size of the header
fseek(bin3ds, findex, SEEK_SET);

while (ftell(bin3ds) < (findex + length - 6))
{

switch (h.id)
{
case VERT_LIST	:
// Load the vertices of the onject
VertexListChunkProcessor(h.len, ftell(bin3ds), objindex);
break;
case LOCAL_COORDS	:
//LocalCoordinatesChunkProcessor(h.len, ftell(bin3ds));
break;
case TEX_VERTS	:
// Load the texture coordinates for the vertices
TexCoordsChunkProcessor(h.len, ftell(bin3ds), objindex);
Objects[objindex].textured = true;
break;
default			:
break;
}

fseek(bin3ds, (h.len - 6), SEEK_CUR);
}

// After we have loaded the vertices we can load the faces
fseek(bin3ds, findex, SEEK_SET);

while (ftell(bin3ds) < (findex + length - 6))
{

switch (h.id)
{
case FACE_DESC	:
// Load the faces of the object
FacesDescriptionChunkProcessor(h.len, ftell(bin3ds), objindex);
break;
default			:
break;
}

fseek(bin3ds, (h.len - 6), SEEK_CUR);
}

// move the file pointer back to where we got it so
// that the ProcessChunk() which we interrupted will read
// from the right place
fseek(bin3ds, findex, SEEK_SET);
}

void Model_3DS::VertexListChunkProcessor(long length, long findex, int objindex)
{
unsigned short numVerts;

// move the file pointer to the beginning of the main
// chunk's data findex + the size of the header
fseek(bin3ds, findex, SEEK_SET);

// Read the number of vertices of the object

// Allocate arrays for the vertices and normals
Objects[objindex].Vertexes = new GLfloat[numVerts * 3];
Objects[objindex].Normals = new GLfloat[numVerts * 3];
Objects[objindex].Tangents = new GLfloat[numVerts * 3];
// Assign the number of vertices for future use
Objects[objindex].numVerts = numVerts;

// Zero out the normals array
for (int j = 0; j < numVerts * 3; j++)
{
Objects[objindex].Normals[j] = 0.0f;
Objects[objindex].Tangents[j] = 0.0f;
}

// Read the vertices, switching the y and z coordinates and changing the sign of the z coordinate
for (int i = 0; i < numVerts * 3; i+=3)
{

// Change the sign of the z coordinate
Objects[objindex].Vertexes[i+2] = -Objects[objindex].Vertexes[i+2];
}

// move the file pointer back to where we got it so
// that the ProcessChunk() which we interrupted will read
// from the right place
fseek(bin3ds, findex, SEEK_SET);
}

void Model_3DS::TexCoordsChunkProcessor(long length, long findex, int objindex)
{
// The number of texture coordinates
unsigned short numCoords;

// move the file pointer to the beginning of the main
// chunk's data findex + the size of the header
fseek(bin3ds, findex, SEEK_SET);

// Read the number of coordinates

// Allocate an array to hold the texture coordinates
Objects[objindex].TexCoords = new GLfloat[numCoords * 2];

// Set the number of texture coords
Objects[objindex].numTexCoords = numCoords;

// Read teh texture coordiantes into the array
for (int i = 0; i < numCoords * 2; i+=2)
{
}

// move the file pointer back to where we got it so
// that the ProcessChunk() which we interrupted will read
// from the right place
fseek(bin3ds, findex, SEEK_SET);
}

void Model_3DS::FacesDescriptionChunkProcessor(long length, long findex, int objindex)
{
unsigned short numFaces;	// The number of faces in the object
unsigned short vertA;		// The first vertex of the face
unsigned short vertB;		// The second vertex of the face
unsigned short vertC;		// The third vertex of the face
unsigned short flags;		// The winding order flags
long subs;					// Holds our place in the file
int numMatFaces = 0;		// The number of different materials

// move the file pointer to the beginning of the main
// chunk's data findex + the size of the header
fseek(bin3ds, findex, SEEK_SET);

// Read the number of faces

// Allocate an array to hold the faces
Objects[objindex].Faces = new GLushort[numFaces * 3];
// Store the number of faces
Objects[objindex].numFaces = numFaces * 3;

// Read the faces into the array
for (int i = 0; i < numFaces * 3; i+=3)
{
// Read the vertices of the face

// Place them in the array
Objects[objindex].Faces   = vertA;
Objects[objindex].Faces[i+1] = vertB;
Objects[objindex].Faces[i+2] = vertC;

// Calculate the face's normal
Vector n;
Vertex v1;
Vertex v2;
Vertex v3;

v1.x = Objects[objindex].Vertexes[vertA*3];
v1.y = Objects[objindex].Vertexes[vertA*3+1];
v1.z = Objects[objindex].Vertexes[vertA*3+2];
v2.x = Objects[objindex].Vertexes[vertB*3];
v2.y = Objects[objindex].Vertexes[vertB*3+1];
v2.z = Objects[objindex].Vertexes[vertB*3+2];
v3.x = Objects[objindex].Vertexes[vertC*3];
v3.y = Objects[objindex].Vertexes[vertC*3+1];
v3.z = Objects[objindex].Vertexes[vertC*3+2];

// calculate the normal
float u[3], v[3];

// V2 - V3;
u[0] = v2.x - v3.x;
u[1] = v2.y - v3.y;
u[2] = v2.z - v3.z;

// V2 - V1;
v[0] = v2.x - v1.x;
v[1] = v2.y - v1.y;
v[2] = v2.z - v1.z;

n.x = (u[1]*v[2] - u[2]*v[1]);
n.y = (u[2]*v[0] - u[0]*v[2]);
n.z = (u[0]*v[1] - u[1]*v[0]);

// Add this normal to its verts' normals
Objects[objindex].Normals[vertA*3]   += n.x;
Objects[objindex].Normals[vertA*3+1] += n.y;
Objects[objindex].Normals[vertA*3+2] += n.z;
Objects[objindex].Normals[vertB*3]   += n.x;
Objects[objindex].Normals[vertB*3+1] += n.y;
Objects[objindex].Normals[vertB*3+2] += n.z;
Objects[objindex].Normals[vertC*3]   += n.x;
Objects[objindex].Normals[vertC*3+1] += n.y;
Objects[objindex].Normals[vertC*3+2] += n.z;

vec3 P1,P2,P3;
vec2 UV1,UV2,UV3;

P1.x = Objects[objindex].Vertexes[vertA*3];
P1.y = Objects[objindex].Vertexes[vertA*3+1];
P1.z = Objects[objindex].Vertexes[vertA*3+2];
P2.x = Objects[objindex].Vertexes[vertB*3];
P2.y = Objects[objindex].Vertexes[vertB*3+1];
P2.z = Objects[objindex].Vertexes[vertB*3+2];
P3.x = Objects[objindex].Vertexes[vertC*3];
P3.y = Objects[objindex].Vertexes[vertC*3+1];
P3.z = Objects[objindex].Vertexes[vertC*3+2];

UV1.x = Objects[objindex].TexCoords[vertA*2];
UV1.y = Objects[objindex].TexCoords[vertA*2+1];
UV2.x = Objects[objindex].TexCoords[vertB*2];
UV2.y = Objects[objindex].TexCoords[vertB*2+1];
UV3.x = Objects[objindex].TexCoords[vertC*2];
UV3.y = Objects[objindex].TexCoords[vertC*2+1];

vec3 Edge1 = P2 - P1;
vec3 Edge2 = P3 - P1;
vec2 Edge1uv = UV2 - UV1;
vec2 Edge2uv = UV3 - UV1;
vec3 tangent;
float cp = Edge1uv.y * Edge2uv.x - Edge1uv.x * Edge2uv.y;

if ( cp != 0.0f ) {
float mul = 1.0f / cp;
tangent   = (Edge1 * -Edge2uv.y + Edge2 * Edge1uv.y) * mul;
}

// Add this normal to its verts' normals
Objects[objindex].Tangents[vertA*3]   += tangent.x;
Objects[objindex].Tangents[vertA*3+1] += tangent.y;
Objects[objindex].Tangents[vertA*3+2] += tangent.z;
Objects[objindex].Tangents[vertB*3]   += tangent.x;
Objects[objindex].Tangents[vertB*3+1] += tangent.y;
Objects[objindex].Tangents[vertB*3+2] += tangent.z;
Objects[objindex].Tangents[vertC*3]   += tangent.x;
Objects[objindex].Tangents[vertC*3+1] += tangent.y;
Objects[objindex].Tangents[vertC*3+2] += tangent.z;

}

// Store our current file position
subs = ftell(bin3ds);

// Check to see how many materials the faces are split into
while (ftell(bin3ds) < (findex + length - 6))
{

switch (h.id)
{
case FACE_MAT	:
//FacesMaterialsListChunkProcessor(h.len, ftell(bin3ds), objindex);
numMatFaces++;
break;
default			:
break;
}

fseek(bin3ds, (h.len - 6), SEEK_CUR);
}

// Split the faces up according to their materials
if (numMatFaces > 0)
{
// Allocate an array to hold the lists of faces divided by material
Objects[objindex].MatFaces = new MaterialFaces[numMatFaces];
// Store the number of material faces
Objects[objindex].numMatFaces = numMatFaces;

fseek(bin3ds, subs, SEEK_SET);

int j = 0;

// Split the faces up
while (ftell(bin3ds) < (findex + length - 6))
{

switch (h.id)
{
case FACE_MAT	:
// Process the faces and split them up
FacesMaterialsListChunkProcessor(h.len, ftell(bin3ds), objindex, j);
j++;
break;
default			:
break;
}

fseek(bin3ds, (h.len - 6), SEEK_CUR);
}
}

// move the file pointer back to where we got it so
// that the ProcessChunk() which we interrupted will read
// from the right place
fseek(bin3ds, findex, SEEK_SET);
}

void Model_3DS::FacesMaterialsListChunkProcessor(long length, long findex, int objindex, int subfacesindex)
{
char name[80];				// The material's name
unsigned short numEntries;	// The number of faces associated with this material
unsigned short Face;		// Holds the faces as they are read
int material;				// An index to the Materials array for this material

// move the file pointer to the beginning of the main
// chunk's data findex + the size of the header
fseek(bin3ds, findex, SEEK_SET);

for (int i = 0; i < 80; i++)
{
name = fgetc(bin3ds);
if (name == 0)
{
name = NULL;
break;
}
}

// Faind the material's index in the Materials array
for (material = 0; material < numMaterials; material++)
{
if (strcmp(name, Materials[material].name) == 0)
break;
}

// Store this value for later so that we can find the material
Objects[objindex].MatFaces[subfacesindex].MatIndex = material;

// Read the number of faces associated with this material

// Allocate an array to hold the list of faces associated with this material
Objects[objindex].MatFaces[subfacesindex].subFaces = new GLushort[numEntries * 3];
// Store this number for later use
Objects[objindex].MatFaces[subfacesindex].numSubFaces = numEntries * 3;

// Read the faces into the array
for (i = 0; i < numEntries * 3; i+=3)
{
// Add the face's vertices to the list
Objects[objindex].MatFaces[subfacesindex].subFaces = Objects[objindex].Faces[Face * 3];
Objects[objindex].MatFaces[subfacesindex].subFaces[i+1] = Objects[objindex].Faces[Face * 3 + 1];
Objects[objindex].MatFaces[subfacesindex].subFaces[i+2] = Objects[objindex].Faces[Face * 3 + 2];
}

// move the file pointer back to where we got it so
// that the ProcessChunk() which we interrupted will read
// from the right place
fseek(bin3ds, findex, SEEK_SET);
}


as a sidenote how would i modify the loader to load bumpmaps aswell.. if u dont know how to fix this maybe u could suggest a better loader... i dont really feel there is any reason to crate an own loader.. due to how much time it requires and that there should be some good free ones out there... ive tried lib3ds... but ive never got that to work... [Edited by - Dragon_Strike on May 17, 2007 12:16:18 PM]

##### Share on other sites
First of all, copy the exact error, but not after finding out on which line exactly it occurs. Second of all, do something about your spelling.

This is just friendly advice so you get more chance on a more useful answer.

##### Share on other sites
Quote:
 Original post by SubotronFirst of all, copy the exact error, but not after finding out on which line exactly it occurs. Second of all, do something about your spelling.This is just friendly advice so you get more chance on a more useful answer.

it says... file: dbgheap.c
line: 346

and yea.. ill work on my spelling...

##### Share on other sites
The error sounds like you are trying to access a null pointer or write to invalid memory. Does it crash when you load or when you draw the file? Can you run the application in debug mode and find the exact line that causes the exception? That will make it much easier to work out the cause.

##### Share on other sites
Quote:
 Original post by weasalmonglerThe error sounds like you are trying to access a null pointer or write to invalid memory. Does it crash when you load or when you draw the file? Can you run the application in debug mode and find the exact line that causes the exception? That will make it much easier to work out the cause.

its when loading the file.. i cant get the line... i get a new window with the error and then the program shuts down.. i dont get any line where the error occurred even in debug mode...

im troubleshooting now and trying to find about where in the load sequence it crashes...

##### Share on other sites
this is what ive got...

VertexListChunkProcessor (however i get the error after closing the program when i disable this one)

thats as far as i get right now...

##### Share on other sites
If it is crashing in VertexListChunkProcessor then make sure that objindex always remains in a valid range for the number of objects actually created. In the debugger set breakpoints in a few places and examine what the values of different variables are, make sure they are all correct. The code you've posted is quite long and is difficult to debug without running it.

##### Share on other sites
well as far as ive been able to debug the problems seems to occur here..

Objects[objindex].Vertexes = new GLfloat[numVerts * 3];
Objects[objindex].Normals = new GLfloat[numVerts * 3];
Objects[objindex].Tangents = new GLfloat[numVerts * 3];

but i dont understand how...

anyways.. im giving up on this... ive tried for a few hours now... but i dont think im good enough to be able to solve this problem..

ill have to search around for another loader...

thx alot for the help anyway...

##### Share on other sites
I'm sure that the problem is that your objindex does not lie in the [0, numObjects-1] range. That would lead to the program trying to write to invalid memory and cause the crash. You just need to work out why objindex isn't the right value.

Sorry to hear you're giving up. Best of luck with it in future.

##### Share on other sites
well on second thought.. if i give up with this i have to give up with 3d programming.. .since this is an important part...

so the hell with it.. ill write my own loader...

ive just begun with it but ive alrdy encountered a problem.. the model doesnt draw properly... im just trying to load a simple box and draw its polygons.. but it gets messed up... i just get to planes facing each other.. they are not connected for some reason

i hope its not to much code to read through.. i have no idea what could be wrong

EDIT:::

i seem to have solved it...

which brings me to the next question.. wher can i find some documentation on more advanced loading? models consisting of several objects... loading the texturenames (diffusemap, bumpmap etc...)..

[Edited by - Dragon_Strike on May 17, 2007 12:19:45 PM]