#include "scene/node.h" #include "compat/misc.h" #include "threads.h" #include "main/references.h" #include "main/logging.h" #include "render/driver.h" #include "obj/object.h" #include "empire.h" #include "physics/physics_world.h" #include "aabbox.h" #include "memory/AllocOnlyPool.h" #include #include extern double frameTime_s; extern unsigned frameNumber; Colorf fallbackNodeColor; Object* fallbackNodeObject; //Produce a vaguely unique value per node, consistent per node void shader_unique(float* values,unsigned short,void*) { if(auto* node = scene::renderingNode) values[0] = (float)(((node - (scene::Node*)0) >> 4) & 0xff) / 255.f; else values[0] = 0.5f; } void shader_node_color(float* colors,unsigned short,void*) { if(auto* node = scene::renderingNode) *(Colorf*)colors = node->color; else *(Colorf*)colors = fallbackNodeColor; } void shader_node_distance(float* distance,unsigned short,void*) { if(auto* node = scene::renderingNode) *distance = (float)node->sortDistance; else *distance = 0.f; } void shader_node_scale(float* scale,unsigned short,void*) { if(auto* node = scene::renderingNode) *scale = (float)node->abs_scale; else *scale = 1.f; } void shader_node_selected(float* value,unsigned short,void*) { if(auto* node = scene::renderingNode) { Object* obj = node->obj; if(obj && obj->getFlag(objSelected)) *(float*)value = 1.f; else *(float*)value = 0.f; } else *(float*)value = 0.f; } void shader_obj_velocity(float* value,unsigned short,void*) { if(auto* node = scene::renderingNode) { Object* obj = node->obj; if(obj) *(float*)value = (obj->velocity + obj->acceleration * (frameTime_s - obj->lastTick)).getLength(); else *(float*)value = 0.f; } else if(fallbackNodeObject) { *(float*)value = (fallbackNodeObject->velocity + fallbackNodeObject->acceleration * (frameTime_s - fallbackNodeObject->lastTick)).getLength(); } else *(float*)value = 0.f; } void shader_obj_position(float* value,unsigned short,void*) { if(auto* node = scene::renderingNode) { Object* obj = node->obj; if(obj) { value[0] = obj->position.x; value[1] = obj->position.y; value[2] = obj->position.z; } else { value[0] = 0.f; value[1] = 0.f; value[2] = 0.f; } } else if(fallbackNodeObject) { value[0] = fallbackNodeObject->position.x; value[1] = fallbackNodeObject->position.y; value[2] = fallbackNodeObject->position.z; } else { value[0] = 0.f; value[1] = 0.f; value[2] = 0.f; } } void shader_obj_rotation(float* value,unsigned short,void*) { if(auto* node = scene::renderingNode) { Object* obj = node->obj; if(obj) { value[0] = obj->rotation.xyz.x; value[1] = obj->rotation.xyz.y; value[2] = obj->rotation.xyz.z; value[3] = obj->rotation.w; } else { value[0] = 0.f; value[1] = 0.f; value[2] = 0.f; value[3] = 1.f; } } else if(fallbackNodeObject) { value[0] = fallbackNodeObject->rotation.xyz.x; value[1] = fallbackNodeObject->rotation.xyz.y; value[2] = fallbackNodeObject->rotation.xyz.z; value[3] = fallbackNodeObject->rotation.w; } else { value[0] = 0.f; value[1] = 0.f; value[2] = 0.f; value[3] = 1.f; } } void shader_node_position(float* value,unsigned short,void*) { if(auto* node = scene::renderingNode) { value[0] = node->abs_position.x; value[1] = node->abs_position.y; value[2] = node->abs_position.z; } else if(fallbackNodeObject) { value[0] = fallbackNodeObject->position.x; value[1] = fallbackNodeObject->position.y; value[2] = fallbackNodeObject->position.z; } else { value[0] = 0.f; value[1] = 0.f; value[2] = 0.f; } } void shader_node_rotation(float* value,unsigned short,void*) { if(auto* node = scene::renderingNode) { value[0] = node->abs_rotation.xyz.x; value[1] = node->abs_rotation.xyz.y; value[2] = node->abs_rotation.xyz.z; value[3] = node->abs_rotation.w; } else if(fallbackNodeObject) { value[0] = fallbackNodeObject->rotation.xyz.x; value[1] = fallbackNodeObject->rotation.xyz.y; value[2] = fallbackNodeObject->rotation.xyz.z; value[3] = fallbackNodeObject->rotation.w; } else { value[0] = 0.f; value[1] = 0.f; value[2] = 0.f; value[3] = 1.f; } } void shader_obj_acceleration(float* value,unsigned short,void*) { if(auto* node = scene::renderingNode) { Object* obj = node->obj; if(obj) *(float*)value = obj->acceleration.getLength(); else *(float*)value = 0.f; } else if(fallbackNodeObject) { *(float*)value = fallbackNodeObject->acceleration.getLength(); } else *(float*)value = 0.f; } void shader_emp_flag(int* value,unsigned short,void*) { if(auto* node = scene::renderingNode) { Object* obj = node->obj; if(obj) { Empire* owner = obj->owner; if(owner) *(int*)value = (int)owner->flagID; else *(int*)value = 0; } else { *(int*)value = 0; } } else if(fallbackNodeObject) { Empire* owner = fallbackNodeObject->owner; if(owner) *(int*)value = (int)owner->flagID; else *(int*)value = 0; } else *(int*)value = 0; } void shader_obj_id(int* value,unsigned short,void*) { if(auto* node = scene::renderingNode) { Object* obj = node->obj; if(obj) *(int*)value = obj->id; else *(int*)value = 0; } else if(fallbackNodeObject) { *(int*)value = fallbackNodeObject->id; } else *(int*)value = 0; } namespace scene { const char* typeNames[NT_ScriptBase] = { "Misc Node", "Culling Node", "Particle System", "Mesh Node", "Mesh+Icon Node" }; extern const char* getScriptNodeName(unsigned id); #ifdef PROFILE_ANIMATION volatile double nodeAnimTimes[32] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; threads::atomic_int nodeAnimCounts[32]; void dumpAnimationProfile() { for(unsigned i = 0; i < 32; ++i) { int count = nodeAnimCounts[i]; double total = nodeAnimTimes[i]; if(count == 0) continue; nodeAnimTimes[i] = 0; nodeAnimCounts[i] = 0; const char* name = nullptr; if(i < NT_ScriptBase) name = typeNames[i]; else name = getScriptNodeName(i - NT_ScriptBase); print("%s: %.1lf ms (%.1lf us per)", name, total * 1.0e3, total * 1.0e6 / double(count)); } } #endif const char* Node::getName() const { unsigned typeIndex = (unsigned)getType(); if(typeIndex < NT_ScriptBase) return typeNames[typeIndex]; else return getScriptNodeName(typeIndex - NT_ScriptBase); } Node* renderingNode; threads::Mutex nodeEventLock; std::vector* pushQueue = new std::vector, *activeQueue = new std::vector; memory::AllocOnlyRegion nodeEventMemory(2048); void* NodeEvent::operator new(size_t bytes) { return nodeEventMemory.alloc(bytes); } void NodeEvent::operator delete(void* p) { nodeEventMemory.dealloc((unsigned char*)p); } void queueNodeEvent(NodeEvent* evt) { nodeEventLock.lock(); pushQueue->push_back(evt); nodeEventLock.release(); } void clearNodeEvents() { nodeEventLock.lock(); for(auto i = activeQueue->begin(), end = activeQueue->end(); i != end; ++i) delete *i; for(auto i = pushQueue->begin(), end = pushQueue->end(); i != end; ++i) delete *i; activeQueue->clear(); pushQueue->clear(); nodeEventLock.release(); } void processNodeEvents() { if(pushQueue->empty()) return; nodeEventLock.lock(); std::swap(pushQueue,activeQueue); nodeEventLock.release(); for(auto i = activeQueue->begin(), end = activeQueue->end(); i != end; ++i) { NodeEvent* evt = *i; evt->process(); delete evt; } activeQueue->clear(); } NodeEvent::NodeEvent(Node* node) : node(node) { if(node) node->grab(); } NodeEvent::~NodeEvent() { if(node) node->drop(); } void Node::markForDeletion() { //TODO: This might be unsafe if the bool is packed alongside another member queuedDelete = true; } class ReparentNode : public NodeEvent { heldPointer parent; public: ReparentNode(Node* reparent, Node* toParent) : NodeEvent(reparent), parent(toParent) { } void process() override { node->setParent(parent); node->rebuildTransformation(); node->sortDistance = node->abs_position.distanceTo(devices.render->cam_pos); } }; void Node::queueReparent(Node* newParent) { if(newParent == 0) newParent = devices.scene; queueNodeEvent(new ReparentNode(this, newParent)); } class ReparentToObject : public NodeEvent { public: heldPointer object; ReparentToObject(Node* reparent, Object* obj) : NodeEvent(reparent), object(obj) { } void process() override { //The object can't be destroyed here, so the node should stay valid too //TODO: Check that assumption in all cases. (True in practice now because //region nodes are never destroyed in game) Node* other = object->node; if(other) { node->setParent(other); node->rebuildTransformation(); node->sortDistance = node->abs_position.distanceTo(devices.render->cam_pos); } } }; void Node::hintParentObject(Object* obj, bool checkDistance) { if(!obj || !obj->node) { //We shouldn't be parented to anything if(parent && parent != devices.scene) queueReparent(nullptr); } else { //Check that we're completely inside this object double maxDist = obj->radius - abs_scale; if(!checkDistance || abs_position.distanceToSQ(obj->position) < maxDist * maxDist) { //Accessing the random object's node pointer is //not technically safe here, but because we're only checking //for equality it won't break. if(parent != obj->node) queueNodeEvent(new ReparentToObject(this, obj)); } else { //We're too big to be in this node, leave if(parent && parent != devices.scene) queueReparent(nullptr); } } if(obj) obj->drop(); } bool Node::getFlag(NodeFlag flag) const { return (flags & flag) != 0; } void Node::setFlag(NodeFlag flag, bool val) { if(val) flags |= flag; else flags &= ~flag; } bool Node::isChild(Node* check) { foreach(child, children) { if(*child == check) return true; } return false; } void Node::addChild(Node* child) { child->grab(); children.push_back(child); child->setFlag(NF_Dirty, true); child->parent = this; } void Node::removeChild(Node* check) { foreach(child, children) { if(*child == check) { children.erase(child); check->parent = 0; check->drop(); return; } } } void Node::setParent(Node* newParent) { if(newParent == parent) return; Node* oldParent = parent; if(newParent) newParent->addChild(this); if(oldParent) oldParent->removeChild(this); parent = newParent; if(newParent && oldParent) animate(); } void Node::destroyTree() { foreach(child, children) (*child)->destroyTree(); children.clear(); parent = 0; } void Node::destroy() { if(physics) { devices.nodePhysics->unregisterItem(*physics); physics = 0; drop(); } if(obj) { obj->drop(); obj = 0; } animator = 0; parent = 0; } void Node::flagDirty() { if(!getFlag(NF_Dirty)) { setFlag(NF_Dirty, true); foreach(child, children) (*child)->flagDirty(); } } bool nodeSort(scene::Node* a, scene::Node* b) { return *a < *b; } void Node::animate() { #ifdef PROFILE_ANIMATION double start = devices.driver->getAccurateTime(); #endif if(animator) animator->animate(this); if(flags & NF_Dirty) rebuildTransformation(); if(flags & NF_AnimOnlyVisible) frameVisible = visible && preRender(*devices.render); else frameVisible = preRender(*devices.render) && visible; #ifdef PROFILE_ANIMATION double end = devices.driver->getAccurateTime(); double duration = end - start; auto type = getType(); nodeAnimCounts[type]++; volatile double& curTime = nodeAnimTimes[type]; while(true) { double prev = curTime; double result = prev + duration; if(threads::compare_and_swap((long long*)&curTime, *(long long*)&prev, *(long long*)&result) == *(long long*)&prev) break; } #endif if(frameVisible) { foreach(child, children) (*child)->animate(); if(children.size() > 1) std::sort(children.begin(), children.end(), nodeSort); } } void Node::sortChildren() { if(children.size() > 1) std::sort(children.begin(), children.end(), nodeSort); } void Node::rebuildTransformation() { setFlag(NF_Dirty, false); if(parent && !getFlag(NF_Independent)) { if(getFlag(NF_ParentScale)) { abs_position = parent->abs_position + (parent->abs_rotation * position) * parent->abs_scale; abs_rotation = parent->abs_rotation * rotation; abs_scale = parent->abs_scale * scale; } else { abs_position = parent->abs_position + position; abs_rotation = rotation; abs_scale = scale; } } else { abs_position = position; abs_rotation = rotation; abs_scale = scale; } if(!getFlag(NF_NoMatrix)) abs_rotation.toTransform(transformation, abs_position, abs_scale); if(physics) { auto bbox = AABBoxd::fromCircle(abs_position, abs_scale); if(physics->bound != bbox) devices.nodePhysics->updateItem(*physics, bbox); } } Node::Node() : flags(NF_Dirty | NF_Independent), lastUpdate(frameTime_s), sortDistance(0), scale(1), abs_scale(1), distanceCutoff(1.0e8), obj(0), physics(0), visible(true), frameVisible(true), queuedDelete(false), remembered(false) {} Node::~Node() { destroy(); } void Node::setObject(Object* Obj) { if(Obj == obj) return; if(Obj) Obj->grab(); if(obj) obj->drop(); obj = Obj; } Object* Node::getObject() { if(obj) obj->grab(); return obj; } void Node::createPhysics() { if(physics || !devices.nodePhysics) return; rebuildTransformation(); grab(); physics = devices.nodePhysics->registerItem(AABBoxd::fromCircle(abs_position, abs_scale), this); } bool Node::operator<(const scene::Node& other) const { if(frameVisible && other.frameVisible) { bool alpha = getFlag(NF_Transparent); if(alpha != other.getFlag(NF_Transparent)) { if(alpha) return false; else return true; } else if(alpha) { return sortDistance > other.sortDistance; } else { return sortDistance < other.sortDistance; } } else if(frameVisible) { return true; } else { return false; } } void Node::_render(render::RenderDriver& driver) { renderingNode = this; render(driver); if(auto cnt = children.size()) { for(decltype(cnt) i = 0; i < cnt;) { scene::Node* child = children[i]; if(!child->queuedDelete) { ++i; if(child->frameVisible) child->_render(driver); } else { child->destroy(); child->drop(); children[i] = children[cnt-1]; --cnt; } } if(cnt != children.size()) children.resize(cnt); } } };