// // Lol Engine // // Copyright: (c) 2010-2012 Sam Hocevar <sam@hocevar.net> // This program is free software; you can redistribute it and/or // modify it under the terms of the Do What The Fuck You Want To // Public License, Version 2, as published by Sam Hocevar. See // http://sam.zoy.org/projects/COPYING.WTFPL for more details. // #if defined HAVE_CONFIG_H # include "config.h" #endif #include "core.h" #include "lolgl.h" #if defined _WIN32 && defined USE_D3D9 # define FAR # define NEAR # include <d3d9.h> #endif using namespace std; #if defined USE_D3D9 extern IDirect3DDevice9 *g_d3ddevice; #elif defined _XBOX extern D3DDevice *g_d3ddevice; #endif namespace lol { // // The VertexBufferData class // -------------------------- // class VertexBufferData { friend class VertexBuffer; friend class VertexDeclaration; size_t m_size; #if defined USE_D3D9 IDirect3DVertexBuffer9 *m_vbo; #elif defined _XBOX D3DVertexBuffer *m_vbo; #else GLuint m_vbo; uint8_t *m_memory; #endif }; // // The VertexDeclaration class // --------------------------- // VertexStreamBase const VertexStreamBase::Empty; VertexDeclaration::VertexDeclaration(VertexStreamBase const &s1, VertexStreamBase const &s2, VertexStreamBase const &s3, VertexStreamBase const &s4, VertexStreamBase const &s5, VertexStreamBase const &s6, VertexStreamBase const &s7, VertexStreamBase const &s8, VertexStreamBase const &s9, VertexStreamBase const &s10, VertexStreamBase const &s11, VertexStreamBase const &s12) : m_count(0) { if (&s1 != &VertexStreamBase::Empty) AddStream(s1); if (&s2 != &VertexStreamBase::Empty) AddStream(s2); if (&s3 != &VertexStreamBase::Empty) AddStream(s3); if (&s4 != &VertexStreamBase::Empty) AddStream(s4); if (&s5 != &VertexStreamBase::Empty) AddStream(s5); if (&s6 != &VertexStreamBase::Empty) AddStream(s6); if (&s7 != &VertexStreamBase::Empty) AddStream(s7); if (&s8 != &VertexStreamBase::Empty) AddStream(s8); if (&s9 != &VertexStreamBase::Empty) AddStream(s9); if (&s10 != &VertexStreamBase::Empty) AddStream(s10); if (&s11 != &VertexStreamBase::Empty) AddStream(s11); if (&s12 != &VertexStreamBase::Empty) AddStream(s12); Initialize(); } VertexDeclaration::~VertexDeclaration() { #if defined _XBOX || defined USE_D3D9 # if defined USE_D3D9 IDirect3DVertexDeclaration9 *vdecl = (IDirect3DVertexDeclaration9 *)m_data; # elif defined _XBOX D3DVertexDeclaration *vdecl = (D3DVertexDeclaration *)m_data; # endif if (FAILED(vdecl->Release())) Abort(); #else #endif } void VertexDeclaration::Bind() { #if defined _XBOX || defined USE_D3D9 # if defined USE_D3D9 IDirect3DVertexDeclaration9 *vdecl = (IDirect3DVertexDeclaration9 *)m_data; # elif defined _XBOX D3DVertexDeclaration *vdecl = (D3DVertexDeclaration *)m_data; # endif if (FAILED(g_d3ddevice->SetVertexDeclaration(vdecl))) Abort(); #else /* FIXME: Nothing to do? */ #endif } void VertexDeclaration::DrawElements(MeshPrimitive type, int skip, int count) { if (count <= 0) return; #if defined _XBOX || defined USE_D3D9 g_d3ddevice->SetRenderState(D3DRS_ALPHABLENDENABLE, 1); g_d3ddevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); g_d3ddevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); if (FAILED(g_d3ddevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW))) Abort(); switch (type) { case MeshPrimitive::Triangles: if (FAILED(g_d3ddevice->DrawPrimitive(D3DPT_TRIANGLELIST, skip, count))) Abort(); break; case MeshPrimitive::Points: if (FAILED(g_d3ddevice->DrawPrimitive(D3DPT_POINTLIST, skip, count))) Abort(); break; } #else glFrontFace(GL_CCW); glEnable(GL_CULL_FACE); # if defined HAVE_GL_2X && !defined __APPLE__ glEnable(GL_ALPHA_TEST); glAlphaFunc(GL_GEQUAL, 0.01f); # endif glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); switch (type) { case MeshPrimitive::Triangles: glDrawArrays(GL_TRIANGLES, skip * 3, count * 3); break; case MeshPrimitive::Points: glDrawArrays(GL_POINTS, skip, count); break; } #endif } void VertexDeclaration::DrawIndexedElements(MeshPrimitive type, int vbase, int vskip, int vcount, int skip, int count) { if (count <= 0) return; #if defined _XBOX || defined USE_D3D9 g_d3ddevice->SetRenderState(D3DRS_ALPHABLENDENABLE, 1); g_d3ddevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); g_d3ddevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); g_d3ddevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW); switch (type) { case MeshPrimitive::Triangles: if (FAILED(g_d3ddevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, vbase, vskip, vcount, skip, count))) Abort(); break; case MeshPrimitive::Points: if (FAILED(g_d3ddevice->DrawIndexedPrimitive(D3DPT_POINTLIST, vbase, vskip, vcount, skip, count))) Abort(); break; } #else # if defined HAVE_GL_2X && !defined __APPLE__ glEnable(GL_ALPHA_TEST); glAlphaFunc(GL_GEQUAL, 0.01f); # endif glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); switch (type) { case MeshPrimitive::Triangles: /* FIXME: ignores most of the arguments! */ glDrawElements(GL_TRIANGLES, count * 3, GL_UNSIGNED_SHORT, 0); break; case MeshPrimitive::Points: /* FIXME: ignores most of the arguments! */ glDrawElements(GL_POINTS, count, GL_UNSIGNED_SHORT, 0); break; } #endif } void VertexDeclaration::Unbind() { #if defined _XBOX || defined USE_D3D9 int stream = -1; for (int i = 0; i < m_count; i++) if (m_streams[i].index != stream) { stream = m_streams[i].index; if (FAILED(g_d3ddevice->SetStreamSource(stream, 0, 0, 0))) Abort(); } if (FAILED(g_d3ddevice->SetVertexDeclaration(NULL))) Abort(); #else /* FIXME: we need to unbind what we bound */ //glDisableVertexAttribArray(m_attrib); /* FIXME: temporary kludge */ for (int i = 0; i < 12; i++) glDisableVertexAttribArray(i); /* FIXME: only useful for VAOs */ //glBindBuffer(GL_ARRAY_BUFFER, 0); /* Or: */ //glDisableVertexAttribArray(m_attrib); /* Or even: */ //glDisableClientState(GL_VERTEX_ARRAY); #endif } void VertexDeclaration::SetStream(VertexBuffer *vb, ShaderAttrib attr1, ShaderAttrib attr2, ShaderAttrib attr3, ShaderAttrib attr4, ShaderAttrib attr5, ShaderAttrib attr6, ShaderAttrib attr7, ShaderAttrib attr8, ShaderAttrib attr9, ShaderAttrib attr10, ShaderAttrib attr11, ShaderAttrib attr12) { if (!vb->m_data->m_size) return; #if defined _XBOX || defined USE_D3D9 /* Only the first item is required to know which stream this * is about; the rest of the information is stored in the * vertex declaration already. */ uint32_t usage = (attr1.m_flags >> 16) & 0xffff; uint32_t index = attr1.m_flags & 0xffff; /* Find the stream number */ int usage_index = 0, stream = -1; for (int i = 0; i < m_count; i++) if (m_streams[i].usage == usage) if (usage_index++ == index) { stream = m_streams[i].index; break; } /* Compute this stream's stride */ int stride = 0; for (int i = 0; i < m_count; i++) if (stream == m_streams[i].index) stride += m_streams[i].size; /* Now we know the stream index and the element stride */ /* FIXME: precompute most of the crap above! */ if (stream >= 0) { if (FAILED(g_d3ddevice->SetStreamSource(stream, vb->m_data->m_vbo, 0, stride))) Abort(); } #else glBindBuffer(GL_ARRAY_BUFFER, vb->m_data->m_vbo); ShaderAttrib l[12] = { attr1, attr2, attr3, attr4, attr5, attr6, attr7, attr8, attr9, attr10, attr11, attr12 }; for (int n = 0; n < 12 && l[n].m_flags != (uint64_t)0 - 1; n++) { uint32_t reg = l[n].m_flags >> 32; uint32_t usage = (l[n].m_flags >> 16) & 0xffff; uint32_t index = l[n].m_flags & 0xffff; # if !defined __CELLOS_LV2__ glEnableVertexAttribArray((GLint)reg); # else switch (usage) { case VertexUsage::Position: glEnableClientState(GL_VERTEX_ARRAY); break; case VertexUsage::Color: glEnableClientState(GL_COLOR_ARRAY); break; } # endif /* We need to parse the whole vertex declaration to retrieve * the information. It sucks. */ int attr_index = 0, usage_index = 0; /* First, find the stream index */ for (; attr_index < m_count; attr_index++) if (m_streams[attr_index].usage == usage) if (usage_index++ == index) break; /* Now compute the stride and offset up to this stream index */ int stride = 0, offset = 0; for (int i = 0; i < m_count; i++) if (m_streams[i].index == m_streams[attr_index].index) { stride += m_streams[i].size; if (i < attr_index) offset += m_streams[i].size; } /* Finally, we need to retrieve the type of the data */ # if !defined GL_DOUBLE # define GL_DOUBLE 0 # endif static struct { GLint size; GLenum type; } const tlut[] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, /* half */ { 1, GL_FLOAT }, { 2, GL_FLOAT }, { 3, GL_FLOAT }, { 4, GL_FLOAT }, /* float */ { 1, GL_DOUBLE }, { 2, GL_DOUBLE }, { 3, GL_DOUBLE }, { 4, GL_DOUBLE }, /* double */ { 1, GL_BYTE }, { 2, GL_BYTE }, { 3, GL_BYTE }, { 4, GL_BYTE }, /* int8_t */ { 1, GL_UNSIGNED_BYTE }, { 2, GL_UNSIGNED_BYTE }, { 3, GL_UNSIGNED_BYTE }, { 4, GL_UNSIGNED_BYTE }, /* uint8_t */ { 1, GL_SHORT }, { 2, GL_SHORT }, { 3, GL_SHORT }, { 4, GL_SHORT }, /* int16_t */ { 1, GL_UNSIGNED_SHORT }, { 2, GL_UNSIGNED_SHORT }, { 3, GL_UNSIGNED_SHORT }, { 4, GL_UNSIGNED_SHORT }, /* uint16_t */ { 1, GL_INT }, { 2, GL_INT }, { 3, GL_INT }, { 4, GL_INT }, /* int32_t */ { 1, GL_UNSIGNED_INT }, { 2, GL_UNSIGNED_INT }, { 3, GL_UNSIGNED_INT }, { 4, GL_UNSIGNED_INT }, /* uint32_t */ }; int type_index = m_streams[attr_index].stream_type; if (type_index < 0 || type_index >= sizeof(tlut) / sizeof(*tlut)) type_index = 0; /* Normalize unsigned bytes by default, because it's usually * some color information. */ GLboolean normalize = (tlut[type_index].type == GL_UNSIGNED_BYTE) || (tlut[type_index].type == GL_BYTE); # if !defined __CELLOS_LV2__ glVertexAttribPointer((GLint)reg, tlut[type_index].size, tlut[type_index].type, normalize, stride, (GLvoid const *)(uintptr_t)offset); # else switch (usage) { case VertexUsage::Position: glVertexPointer(tlut[type_index].size, tlut[type_index].type, stride, (GLvoid const *)(uintptr_t)offset); break; case VertexUsage::Normal: glNormalPointer(tlut[type_index].type, stride, (GLvoid const *)(uintptr_t)offset); break; case VertexUsage::Color: glColorPointer(tlut[type_index].size, tlut[type_index].type, stride, (GLvoid const *)(uintptr_t)offset); break; } # endif } #endif } void VertexDeclaration::Initialize() { #if defined _XBOX || defined USE_D3D9 static D3DVERTEXELEMENT9 const end_element[] = { D3DDECL_END() }; static D3DDECLTYPE const X = D3DDECLTYPE_UNUSED; static D3DDECLTYPE const tlut[] = { D3DDECLTYPE_UNUSED, X, D3DDECLTYPE_FLOAT16_2, X, D3DDECLTYPE_FLOAT16_4, /* half */ D3DDECLTYPE_FLOAT1, D3DDECLTYPE_FLOAT2, D3DDECLTYPE_FLOAT3, D3DDECLTYPE_FLOAT4, /* float */ X, X, X, X, /* double */ X, X, X, X, /* int8_t */ X, X, X, D3DDECLTYPE_UBYTE4N, /* uint8_t */ X, D3DDECLTYPE_SHORT2N, X, D3DDECLTYPE_SHORT4N, /* int16_t */ X, D3DDECLTYPE_USHORT2N, X, D3DDECLTYPE_USHORT4N, /* uint16_t */ X, X, X, X, /* int32_t */ X, X, X, X, /* uint32_t */ }; static D3DDECLUSAGE const ulut[] = { D3DDECLUSAGE_POSITION, D3DDECLUSAGE_BLENDWEIGHT, D3DDECLUSAGE_BLENDINDICES, D3DDECLUSAGE_NORMAL, D3DDECLUSAGE_PSIZE, D3DDECLUSAGE_TEXCOORD, D3DDECLUSAGE_TANGENT, D3DDECLUSAGE_BINORMAL, D3DDECLUSAGE_TESSFACTOR, #if defined _XBOX D3DDECLUSAGE_TEXCOORD, /* FIXME: nonexistent */ #else D3DDECLUSAGE_POSITIONT, #endif D3DDECLUSAGE_COLOR, D3DDECLUSAGE_FOG, D3DDECLUSAGE_DEPTH, D3DDECLUSAGE_SAMPLE, }; D3DVERTEXELEMENT9 elements[12 + 1]; for (int n = 0; n < m_count; n++) { elements[n].Stream = m_streams[n].index; elements[n].Offset = 0; for (int i = 0; i < n; i++) if (m_streams[i].index == m_streams[n].index) elements[n].Offset += m_streams[i].size; if (m_streams[n].stream_type >= 0 && m_streams[n].stream_type < sizeof(tlut) / sizeof(*tlut)) elements[n].Type = tlut[m_streams[n].stream_type]; else elements[n].Type = D3DDECLTYPE_UNUSED; elements[n].Method = D3DDECLMETHOD_DEFAULT; if (m_streams[n].usage >= 0 && m_streams[n].usage < sizeof(ulut) / sizeof(*ulut)) elements[n].Usage = ulut[m_streams[n].usage]; else elements[n].Usage = D3DDECLUSAGE_POSITION; elements[n].UsageIndex = 0; for (int i = 0; i < n; i++) if (elements[i].Stream == elements[n].Stream && elements[i].Usage == elements[n].Usage) elements[n].UsageIndex++; } elements[m_count] = end_element[0]; # if defined USE_D3D9 IDirect3DVertexDeclaration9 *vdecl; # elif defined _XBOX D3DVertexDeclaration *vdecl; # endif if (FAILED(g_d3ddevice->CreateVertexDeclaration(elements, &vdecl))) Abort(); m_data = vdecl; #else #endif } void VertexDeclaration::AddStream(VertexStreamBase const &s) { int index = m_count ? m_streams[m_count - 1].index + 1 : 0; for (int i = 0; s.m_streams[i].size; i++) { m_streams[m_count].stream_type = s.m_streams[i].stream_type; m_streams[m_count].usage = s.m_streams[i].usage; m_streams[m_count].size = s.m_streams[i].size; m_streams[m_count].index = index; m_count++; } } // // The VertexBuffer class // ---------------------- // VertexBuffer::VertexBuffer(size_t size) : m_data(new VertexBufferData) { m_data->m_size = size; if (!size) return; #if defined USE_D3D9 || defined _XBOX if (FAILED(g_d3ddevice->CreateVertexBuffer(size, D3DUSAGE_WRITEONLY, NULL, D3DPOOL_MANAGED, &m_data->m_vbo, NULL))) Abort(); #elif !defined __CELLOS_LV2__ glGenBuffers(1, &m_data->m_vbo); m_data->m_memory = new uint8_t[size]; #endif } VertexBuffer::~VertexBuffer() { if (m_data->m_size) { #if defined USE_D3D9 || defined _XBOX if (FAILED(m_data->m_vbo->Release())) Abort(); #elif !defined __CELLOS_LV2__ glDeleteBuffers(1, &m_data->m_vbo); delete[] m_data->m_memory; #endif } delete m_data; } void *VertexBuffer::Lock(size_t offset, size_t size) { if (!m_data->m_size) return NULL; #if defined USE_D3D9 || defined _XBOX void *ret; if (FAILED(m_data->m_vbo->Lock(offset, size, (void **)&ret, 0))) Abort(); return ret; #elif !defined __CELLOS_LV2__ return m_data->m_memory + offset; #endif } void VertexBuffer::Unlock() { if (!m_data->m_size) return; #if defined USE_D3D9 || defined _XBOX if (FAILED(m_data->m_vbo->Unlock())) Abort(); #elif !defined __CELLOS_LV2__ glBindBuffer(GL_ARRAY_BUFFER, m_data->m_vbo); glBufferData(GL_ARRAY_BUFFER, m_data->m_size, m_data->m_memory, GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); #endif } } /* namespace lol */