void Update() override final {
		if (lastActivated >= 0) {
			InputPort<AnyType>* port = static_cast<InputPort<AnyType>*>(GetInput(lastActivated));
			GetOutput<0>().Set(*port->Get());
			lastActivated = -1;
		}
	}
	void Update() override final {
		if (lastActivated >= 0 && activationMap == 0b11'1111) {
			InputPort<AnyType>* port = static_cast<InputPort<AnyType>*>(GetInput(lastActivated+1));
			GetOutput<0>().Set(*port->Get());
		}
	}
Beispiel #3
0
void MaterialShaderGraph::AssembleShaderCode() {
	std::vector<ShaderNode> shaderNodes(m_nodes.size());
	std::vector<std::vector<MaterialShaderParameter>> shaderNodeParams(m_nodes.size());
	std::vector<eMaterialShaderParamType> shaderNodeReturns(m_nodes.size());
	std::vector<std::string> functions(m_nodes.size());

	// collect individual shader codes and set number of input params for each node
	for (size_t i = 0; i < m_nodes.size(); ++i) {
		MaterialShader* shader = m_nodes[i].get();
		functions[i] = shader->GetShaderCode();
		ExtractShaderParameters(functions[i], "main", shaderNodeReturns[i], shaderNodeParams[i]);

		for (auto p : shaderNodeParams[i]) {
			if (p.type == eMaterialShaderParamType::UNKNOWN) {
				throw InvalidArgumentException("Parameter of unknown type.");
			}
		}

		shaderNodes[i].SetNumInputs(shaderNodeParams[i].size());
	}

	// link nodes together
	for (const auto& link : m_links) {
		ShaderNode& source = shaderNodes[link.sourceNode];
		ShaderNode& sink = shaderNodes[link.sinkNode];
		if (sink.GetNumInputs() <= link.sinkPort) {
			throw InvalidArgumentException("Invalid link: port does not have that many inputs.");
		}
		bool isLinked = source.GetOutput(0)->Link(sink.GetInput(link.sinkPort));
		if (!isLinked) {
			throw InvalidArgumentException("Invalid link: duplicate input to a single port.");
		}
	}

	// create output port LUT
	std::unordered_map<OutputPortBase*, size_t> outputLut;
	for (size_t i = 0; i < shaderNodes.size(); ++i) {
		outputLut.insert({ shaderNodes[i].GetOutput(0), i });
	}

	// get the sink node
	size_t sinkNodeIdx = -1;
	for (size_t i = 0; i < shaderNodes.size(); ++i) {
		if (shaderNodes[i].GetOutput(0)->begin() == shaderNodes[i].GetOutput(0)->end()) {
			if (sinkNodeIdx != -1) {
				throw InvalidArgumentException("Invalid graph: multiple sink nodes.");
			}
			sinkNodeIdx = i;
		}
	}
	if (sinkNodeIdx == -1) {
		throw InvalidArgumentException("Invalid graph: no sink nodes, contains circle.");
	}

	// run backwards DFS from sink node
	// - get topological order
	// - get list of free params
	struct FreeParam {
		size_t node, input;
		std::string name;
	};
	std::vector<size_t> topologicalOrder;
	std::vector<bool> visited(shaderNodes.size(), false);
	std::vector<FreeParam> freeParams;
	auto VisitNode = [&](size_t node, auto& self) {
		if (visited[node]) {
			return;
		}
		visited[node] = true;

		for (int i = 0; i < shaderNodes[node].GetNumInputs(); ++i) {
			auto* input = shaderNodes[node].GetInput(i);
			if (input->GetLink() != nullptr) {
				auto* linkOutput = input->GetLink();
				size_t linkNode = outputLut[linkOutput];
				self(linkNode, self);
			}
			else {
				std::stringstream ss;
				ss << m_nodes[node]->GetName() << "__" << shaderNodeParams[node][i].name;
				static_cast<InputPort<std::string>*>(input)->Set(ss.str());
				freeParams.push_back({ node, (size_t)i, ss.str() });
			}
		}

		std::stringstream ss;
		ss << "main_" << topologicalOrder.size();
		shaderNodes[node].SetFunctionName(ss.str());
		functions[node] = std::regex_replace(functions[node], std::regex("main"), ss.str());
		topologicalOrder.push_back(node);
		shaderNodes[node].SetFunctionReturn(GetParameterString(shaderNodeReturns[node]));
	};

	// attach final input port to sink node, and update according to topological order
	InputPort<std::string> finalCodePort;
	shaderNodes[sinkNodeIdx].GetOutput(0)->Link(&finalCodePort);
	VisitNode(sinkNodeIdx, VisitNode);
	for (auto idx : topologicalOrder) {
		shaderNodes[idx].Update();
	}

	// assemble resulting code
	std::stringstream finalCode;
	// sub-functions
	for (auto node : topologicalOrder) {
		finalCode << functions[node] << "\n";
	}
	finalCode << "\n\n";
	// signature
	std::string returnType = GetParameterString(shaderNodeReturns[*--topologicalOrder.end()]);
	finalCode << returnType << " main(";
	bool firstParam = true;
	for (auto& p : freeParams) {
		if (!firstParam)
			finalCode << ", ";
		finalCode << GetParameterString(shaderNodeParams[p.node][p.input].type) << " ";
		finalCode << p.name;
		firstParam = false;
	}
	finalCode << ") {\n";
	finalCode << "\n";
	// preambles
	for (auto idx : topologicalOrder) {
		finalCode << "    " << shaderNodes[idx].GetPreamble() << "\n";
	}
	finalCode << "\n";
	// return statement
	finalCode << "return " << finalCodePort.Get() << ";\n";
	finalCode << "}\n";


	m_source = finalCode.str();
}