#include "line_trail_node.h" #include "render/driver.h" #include "compat/gl.h" #include "main/references.h" #include "render/vertexBuffer.h" extern double frameLen_s; extern double pixelSizeRatio; namespace render { extern const RenderMesh* lastRenderedMesh; }; namespace scene { LineTrailNode::LineTrailNode(const render::RenderState& material) : storedPositions(0), firstIndex(1), stored_s(0), lineLen_s(1), qualitySteps(1), mat(material) { setFlag(NF_NoMatrix, true); setFlag(NF_Independent, false); } bool LineTrailNode::preRender(render::RenderDriver& driver) { rebuildTransformation(); stored_s += frameLen_s; double step = lineLen_s / double(LINE_POS_COUNT-1); if(stored_s >= step) { stored_s -= step; --firstIndex; if(firstIndex >= LINE_POS_COUNT) firstIndex = LINE_POS_COUNT - 1; if(storedPositions < LINE_POS_COUNT) ++storedPositions; prevPositions[firstIndex] = abs_position; } sortDistance = abs_position.distanceTo(driver.cam_pos); return sortDistance < (3500.0 * pixelSizeRatio); } void LineTrailNode::render(render::RenderDriver& driver) { auto* vertBuffer = render::VertexBufferTCV::fetch(&mat); auto* v = vertBuffer->request(storedPositions + 1, render::PT_LineStrip); float startPct = (float)(stored_s / lineLen_s); auto& camPos = driver.cam_pos; v[0].pos = vec3f(abs_position - camPos); v[0].col = startCol; v[0].uv = vec2f(0, 0); for(unsigned i = 0; i < storedPositions; ++i) { auto& vert = v[i+1]; unsigned index = (firstIndex + i) % LINE_POS_COUNT; vert.pos = vec3f(prevPositions[index] - camPos); float pct = startPct + (float(i) / float(LINE_POS_COUNT-1)); vert.col = startCol.getInterpolated(endCol, pct); vert.uv = vec2f(pct, float(index) / float(LINE_POS_COUNT)); } } ProjectileBatch::ProjectileBatch() { setFlag(NF_Transparent, true); sortDistance = 0.0; } ProjectileBatch::~ProjectileBatch() { for(auto i = projectiles.begin(), end = projectiles.end(); i != end; ++i) { auto* projs = i->second; if(!projs) continue; for(unsigned ind = 0, cnt = projs->size(); ind != cnt; ++ind) { auto& proj = projs->at(ind); proj.kill->drop(); } delete projs; } } void ProjectileBatch::registerProj(const render::RenderState& mat, ProjEffect& eff) { auto*& projs = projectiles[&mat]; if(!projs) projs = new std::vector; projs->push_back(eff); } bool ProjectileBatch::preRender(render::RenderDriver& driver) { if(projectiles.empty()) return false; float time = (float)frameLen_s; for(auto i = projectiles.begin(), end = projectiles.end(); i != end; ++i) { auto* projs = i->second; if(!projs || projs->empty()) continue; auto* pProj = &(*projs)[0]; unsigned copyOff = 0; for(unsigned ind = 0, cnt = projs->size(); ind != cnt; ++ind) { auto& proj = pProj[ind]; proj.life -= time; if(proj.life < 0 || **proj.kill) { copyOff += 1; proj.kill->drop(); } else { proj.pos += vec3d(proj.dir * (proj.speed * time)); if(copyOff) (*projs)[ind - copyOff] = proj; } } if(copyOff) projs->resize(projs->size() - copyOff); } return true; } void ProjectileBatch::render(render::RenderDriver& driver) { auto& camPos = driver.cam_pos; for(auto i = projectiles.begin(), end = projectiles.end(); i != end; ++i) { auto* projs = i->second; if(!projs) continue; unsigned count = projs->size(); if(count == 0) continue; if(projs->begin()->line) { //Draw out projectiles in batches of 128 unsigned start = 0; while(start < count) { unsigned drawCount; if(start + 128 <= count) drawCount = 128; else drawCount = count - start; auto* buffer = render::VertexBufferTCV::fetch(i->first); auto* verts = buffer->request(drawCount, render::PT_Lines); for(unsigned p = start; p < start + drawCount; ++p, verts += 2) { auto& proj = (*projs)[p]; float alpha = std::min(1.f, proj.life / proj.fadeStart); verts[0].pos = vec3f(proj.pos - camPos); verts[0].uv = vec2f(0,0); verts[0].col = proj.start; verts[0].col.a *= alpha; verts[1].pos = vec3f(proj.pos - vec3d(proj.dir * proj.length) - camPos); verts[1].uv = vec2f(1,0); verts[1].col = proj.end; verts[1].col.a *= alpha; } start += drawCount; } } else { auto& cam_pos = driver.cam_pos; double aspect = 1.0; if(i->first->textures[0]) { auto size = i->first->textures[0]->size; if(size.x > 0 && size.y > 0) aspect = (float)size.y / (float)size.x; } //Draw out projectiles in batches of 64 unsigned start = 0; while(start < count) { unsigned drawCount; if(start + 64 <= count) drawCount = 64; else drawCount = count - start; auto* buffer = render::VertexBufferTCV::fetch(i->first); auto* verts = buffer->request(drawCount, render::PT_Quads); for(unsigned p = start; p < start + drawCount; ++p, verts += 4) { auto& proj = (*projs)[p]; float alpha = std::min(1.f, proj.life / proj.fadeStart); double size = aspect * proj.length * 0.5; vec3d end = proj.pos - vec3d(proj.dir * proj.length); vec3d offset = vec3d(proj.pos - end).cross(driver.cam_facing).normalized(size); Color col = color; verts[0].pos = vec3f(end + offset - cam_pos); verts[0].uv = vec2f(0,0); verts[0].col = proj.end; verts[0].col.a *= alpha; verts[1].pos = vec3f(proj.pos + offset - cam_pos); verts[1].uv = vec2f(1.f,0); verts[1].col = proj.start; verts[1].col.a *= alpha; verts[2].pos = vec3f(proj.pos - offset - cam_pos); verts[2].uv = vec2f(1.f,1.f); verts[2].col = proj.start; verts[2].col.a *= alpha; verts[3].pos = vec3f(end - offset - cam_pos); verts[3].uv = vec2f(0,1.f); verts[3].col = proj.end; verts[3].col.a *= alpha; } start += drawCount; } } } } MissileBatch::MissileBatch() { setFlag(NF_Transparent, true); sortDistance = 0.0; } MissileBatch::~MissileBatch() { for(auto i = missiles.begin(), end = missiles.end(); i != end; ++i) { auto* msls = i->second; if(!msls) continue; for(unsigned ind = 0, cnt = msls->size(); ind != cnt; ++ind) { auto& missile = msls->at(ind); missile.track->drop(); } delete msls; } } void MissileBatch::registerProj(const render::RenderState& mat, const render::RenderState& trail, MissileTrail& eff) { eff.lineStart = eff.lineCount = 0; eff.lineProgress = eff.startProgress = 0; MissileTrailMats mats = {&mat, &trail}; auto*& msls = missiles[mats]; if(!msls) msls = new std::vector; msls->push_back(eff); } bool MissileBatch::preRender(render::RenderDriver& driver) { if(missiles.empty()) return false; if(frameLen_s == 0) return true; double gameTime = devices.driver->getGameTime(); for(auto i = missiles.begin(), end = missiles.end(); i != end; ++i) { auto* msls = i->second; if(!msls) continue; unsigned copyOff = 0; for(unsigned ind = 0, cnt = msls->size(); ind != cnt; ++ind) { auto& missile = (*msls)[ind]; auto& track = **missile.track; if(track.aliveUntil >= 0 && gameTime > track.aliveUntil) { if((int)missile.lineCount <= (int)missile.startProgress) { copyOff += 1; missile.track->drop(); } else { missile.startProgress += frameLen_s * float(LINE_POS_COUNT) / missile.length; if(copyOff) (*msls)[ind - copyOff] = missile; } } else { missile.pos = (missile.pos).interpolate(track.pos + vec3d(track.vel) * (gameTime - track.lastUpdate), std::min(frameLen_s / 0.25, 1.0)); missile.lastUpdate = gameTime; missile.lineProgress += frameLen_s * float(LINE_POS_COUNT) / missile.length; while(missile.lineProgress >= 1.f) { missile.lineProgress -= 1.f; missile.lineCount += 1; missile.lineStart = (missile.lineStart + LINE_POS_COUNT - 1) % LINE_POS_COUNT; if(missile.lineCount > LINE_POS_COUNT) missile.lineCount = LINE_POS_COUNT; missile.trail[missile.lineStart] = missile.pos; } if(copyOff) (*msls)[ind - copyOff] = missile; } } if(copyOff) msls->resize(msls->size() - copyOff); } return true; } void MissileBatch::render(render::RenderDriver& driver) { double gameTime = devices.driver->getGameTime(); for(auto i = missiles.begin(), end = missiles.end(); i != end; ++i) { auto* msls = i->second; if(!msls) continue; //Render all sprites for(unsigned ind = 0, cnt = msls->size(); ind != cnt; ++ind) { auto& missile = (*msls)[ind]; double until = missile.track->data.aliveUntil; if(until < 0 || until > gameTime) devices.render->drawBillboard(missile.pos, missile.size, *i->first.sprite, 0.0, &missile.color); } auto& cam_pos = driver.cam_pos; //Then render all trails for(unsigned ind = 0, cnt = msls->size(); ind != cnt; ++ind) { auto& missile = (*msls)[ind]; if(missile.lineCount == 0) continue; auto* vertBuffer = render::VertexBufferTCV::fetch(i->first.trail); auto* v = vertBuffer->request(missile.lineCount + 1, render::PT_LineStrip); float startPct = (missile.startProgress + missile.lineProgress) * missile.length / float(LINE_POS_COUNT); v[0].pos = vec3f(missile.pos - cam_pos); v[0].col = missile.start; v[0].uv = vec2f(missile.startProgress * missile.length / float(LINE_POS_COUNT), 0); for(unsigned i = 0; i < missile.lineCount; ++i) { auto& vert = v[i+1]; unsigned index = (missile.lineStart + i) % LINE_POS_COUNT; vert.pos = vec3f(missile.trail[index] - cam_pos); float pct = startPct + (float(i) / float(LINE_POS_COUNT-1)); if(pct > 1.f) pct = 1.f; vert.col = missile.start.getInterpolated(missile.end, pct); vert.uv = vec2f(pct, float(index) / float(LINE_POS_COUNT)); } } } } };