aboutsummaryrefslogtreecommitdiff
path: root/src/client/render/interlaced.cpp
blob: 3f79a8eb57909bff21a34058b765ad1ef7fd9728 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
/*
Minetest
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2017 numzero, Lobachevskiy Vitaliy <numzer0@yandex.ru>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include "interlaced.h"
#include "client/client.h"
#include "client/shader.h"
#include "client/tile.h"

RenderingCoreInterlaced::RenderingCoreInterlaced(
	IrrlichtDevice *_device, Client *_client, Hud *_hud)
	: RenderingCoreStereo(_device, _client, _hud)
{
	initMaterial();
}

void RenderingCoreInterlaced::initMaterial()
{
	IShaderSource *s = client->getShaderSource();
	mat.UseMipMaps = false;
	mat.ZBuffer = false;
#if IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR > 8
	mat.ZWriteEnable = video::EZW_OFF;
#else
	mat.ZWriteEnable = false;
#endif
	u32 shader = s->getShader("3d_interlaced_merge", TILE_MATERIAL_BASIC);
	mat.MaterialType = s->getShaderInfo(shader).material;
	for (int k = 0; k < 3; ++k) {
		mat.TextureLayer[k].AnisotropicFilter = false;
		mat.TextureLayer[k].BilinearFilter = false;
		mat.TextureLayer[k].TrilinearFilter = false;
		mat.TextureLayer[k].TextureWrapU = video::ETC_CLAMP_TO_EDGE;
		mat.TextureLayer[k].TextureWrapV = video::ETC_CLAMP_TO_EDGE;
	}
}

void RenderingCoreInterlaced::initTextures()
{
	v2u32 image_size{screensize.X, screensize.Y / 2};
	left = driver->addRenderTargetTexture(
			image_size, "3d_render_left", video::ECF_A8R8G8B8);
	right = driver->addRenderTargetTexture(
			image_size, "3d_render_right", video::ECF_A8R8G8B8);
	mask = driver->addTexture(screensize, "3d_render_mask", video::ECF_A8R8G8B8);
	initMask();
	mat.TextureLayer[0].Texture = left;
	mat.TextureLayer[1].Texture = right;
	mat.TextureLayer[2].Texture = mask;
}

void RenderingCoreInterlaced::clearTextures()
{
	driver->removeTexture(left);
	driver->removeTexture(right);
	driver->removeTexture(mask);
}

void RenderingCoreInterlaced::initMask()
{
	u8 *data = reinterpret_cast<u8 *>(mask->lock());
	for (u32 j = 0; j < screensize.Y; j++) {
		u8 val = j % 2 ? 0xff : 0x00;
		memset(data, val, 4 * screensize.X);
		data += 4 * screensize.X;
	}
	mask->unlock();
}

void RenderingCoreInterlaced::drawAll()
{
	renderBothImages();
	merge();
	drawHUD();
}

void RenderingCoreInterlaced::merge()
{
	static const video::S3DVertex vertices[4] = {
			video::S3DVertex(1.0, -1.0, 0.0, 0.0, 0.0, -1.0,
					video::SColor(255, 0, 255, 255), 1.0, 0.0),
			video::S3DVertex(-1.0, -1.0, 0.0, 0.0, 0.0, -1.0,
					video::SColor(255, 255, 0, 255), 0.0, 0.0),
			video::S3DVertex(-1.0, 1.0, 0.0, 0.0, 0.0, -1.0,
					video::SColor(255, 255, 255, 0), 0.0, 1.0),
			video::S3DVertex(1.0, 1.0, 0.0, 0.0, 0.0, -1.0,
					video::SColor(255, 255, 255, 255), 1.0, 1.0),
	};
	static const u16 indices[6] = {0, 1, 2, 2, 3, 0};
	driver->setMaterial(mat);
	driver->drawVertexPrimitiveList(&vertices, 4, &indices, 2);
}

void RenderingCoreInterlaced::useEye(bool _right)
{
	driver->setRenderTarget(_right ? right : left, true, true, skycolor);
	RenderingCoreStereo::useEye(_right);
}

void RenderingCoreInterlaced::resetEye()
{
	driver->setRenderTarget(nullptr, false, false, skycolor);
	RenderingCoreStereo::resetEye();
}
">"Begin log output over terminal" << " (no stdout/stderr backlog during that)" << std::endl; // Make the loggers to stdout/stderr shut up. // Go over our own loggers instead. LogLevelMask err_mask = g_logger.removeOutput(&stderr_output); LogLevelMask out_mask = g_logger.removeOutput(&stdout_output); g_logger.addOutput(&m_log_output); // Inform the server of our nick m_chat_interface->command_queue.push_back( new ChatEventNick(CET_NICK_ADD, m_nick)); { // Ensures that curses is deinitialized even on an exception being thrown CursesInitHelper helper(this); while (!stopRequested()) { int ch = getch(); if (stopRequested()) break; step(ch); } } if (m_kill_requested) *m_kill_requested = true; g_logger.removeOutput(&m_log_output); g_logger.addOutputMasked(&stderr_output, err_mask); g_logger.addOutputMasked(&stdout_output, out_mask); std::cout << "End log output over terminal" << " (no stdout/stderr backlog during that)" << std::endl; std::cout << "========================" << std::endl; END_DEBUG_EXCEPTION_HANDLER return NULL; } void TerminalChatConsole::typeChatMessage(const std::wstring &msg) { // Discard empty line if (msg.empty()) return; // Send to server m_chat_interface->command_queue.push_back( new ChatEventChat(m_nick, msg)); // Print if its a command (gets eaten by server otherwise) if (msg[0] == L'/') { m_chat_backend.addMessage(L"", (std::wstring)L"Issued command: " + msg); } } void TerminalChatConsole::handleInput(int ch, bool &complete_redraw_needed) { // Helpful if you want to collect key codes that aren't documented /*if (ch != ERR) { m_chat_backend.addMessage(L"", (std::wstring)L"Pressed key " + utf8_to_wide( std::string(keyname(ch)) + " (code " + itos(ch) + ")")); complete_redraw_needed = true; }//*/ // All the key codes below are compatible to xterm // Only add new ones if you have tried them there, // to ensure compatibility with not just xterm but the wide // range of terminals that are compatible to xterm. switch (ch) { case ERR: // no input break; case 27: // ESC // Toggle ESC mode m_esc_mode = !m_esc_mode; break; case KEY_PPAGE: m_chat_backend.scrollPageUp(); complete_redraw_needed = true; break; case KEY_NPAGE: m_chat_backend.scrollPageDown(); complete_redraw_needed = true; break; case KEY_ENTER: case '\r': case '\n': { std::wstring text = m_chat_backend.getPrompt().submit(); typeChatMessage(text); break; } case KEY_UP: m_chat_backend.getPrompt().historyPrev(); break; case KEY_DOWN: m_chat_backend.getPrompt().historyNext(); break; case KEY_LEFT: // Left pressed // move character to the left m_chat_backend.getPrompt().cursorOperation( ChatPrompt::CURSOROP_MOVE, ChatPrompt::CURSOROP_DIR_LEFT, ChatPrompt::CURSOROP_SCOPE_CHARACTER); break; case 545: // Ctrl-Left pressed // move word to the left m_chat_backend.getPrompt().cursorOperation( ChatPrompt::CURSOROP_MOVE, ChatPrompt::CURSOROP_DIR_LEFT, ChatPrompt::CURSOROP_SCOPE_WORD); break; case KEY_RIGHT: // Right pressed // move character to the right m_chat_backend.getPrompt().cursorOperation( ChatPrompt::CURSOROP_MOVE, ChatPrompt::CURSOROP_DIR_RIGHT, ChatPrompt::CURSOROP_SCOPE_CHARACTER); break; case 560: // Ctrl-Right pressed // move word to the right m_chat_backend.getPrompt().cursorOperation( ChatPrompt::CURSOROP_MOVE, ChatPrompt::CURSOROP_DIR_RIGHT, ChatPrompt::CURSOROP_SCOPE_WORD); break; case KEY_HOME: // Home pressed // move to beginning of line m_chat_backend.getPrompt().cursorOperation( ChatPrompt::CURSOROP_MOVE, ChatPrompt::CURSOROP_DIR_LEFT, ChatPrompt::CURSOROP_SCOPE_LINE); break; case KEY_END: // End pressed // move to end of line m_chat_backend.getPrompt().cursorOperation( ChatPrompt::CURSOROP_MOVE, ChatPrompt::CURSOROP_DIR_RIGHT, ChatPrompt::CURSOROP_SCOPE_LINE); break; case KEY_BACKSPACE: case '\b': case 127: // Backspace pressed // delete character to the left m_chat_backend.getPrompt().cursorOperation( ChatPrompt::CURSOROP_DELETE, ChatPrompt::CURSOROP_DIR_LEFT, ChatPrompt::CURSOROP_SCOPE_CHARACTER); break; case KEY_DC: // Delete pressed // delete character to the right m_chat_backend.getPrompt().cursorOperation( ChatPrompt::CURSOROP_DELETE, ChatPrompt::CURSOROP_DIR_RIGHT, ChatPrompt::CURSOROP_SCOPE_CHARACTER); break; case 519: // Ctrl-Delete pressed // delete word to the right m_chat_backend.getPrompt().cursorOperation( ChatPrompt::CURSOROP_DELETE, ChatPrompt::CURSOROP_DIR_RIGHT, ChatPrompt::CURSOROP_SCOPE_WORD); break; case 21: // Ctrl-U pressed // kill line to left end m_chat_backend.getPrompt().cursorOperation( ChatPrompt::CURSOROP_DELETE, ChatPrompt::CURSOROP_DIR_LEFT, ChatPrompt::CURSOROP_SCOPE_LINE); break; case 11: // Ctrl-K pressed // kill line to right end m_chat_backend.getPrompt().cursorOperation( ChatPrompt::CURSOROP_DELETE, ChatPrompt::CURSOROP_DIR_RIGHT, ChatPrompt::CURSOROP_SCOPE_LINE); break; case KEY_TAB: // Tab pressed // Nick completion m_chat_backend.getPrompt().nickCompletion(m_nicks, false); break; default: // Add character to the prompt, // assuming UTF-8. if (IS_UTF8_MULTB_START(ch)) { m_pending_utf8_bytes.append(1, (char)ch); m_utf8_bytes_to_wait += UTF8_MULTB_START_LEN(ch) - 1; } else if (m_utf8_bytes_to_wait != 0) { m_pending_utf8_bytes.append(1, (char)ch); m_utf8_bytes_to_wait--; if (m_utf8_bytes_to_wait == 0) { std::wstring w = utf8_to_wide(m_pending_utf8_bytes); m_pending_utf8_bytes = ""; // hopefully only one char in the wstring... for (size_t i = 0; i < w.size(); i++) { m_chat_backend.getPrompt().input(w.c_str()[i]); } } } else if (IS_ASCII_PRINTABLE_CHAR(ch)) { m_chat_backend.getPrompt().input(ch); } else { // Silently ignore characters we don't handle //warningstream << "Pressed invalid character '" // << keyname(ch) << "' (code " << itos(ch) << ")" << std::endl; } break; } } void TerminalChatConsole::step(int ch) { bool complete_redraw_needed = false; // empty queues while (!m_chat_interface->outgoing_queue.empty()) { ChatEvent *evt = m_chat_interface->outgoing_queue.pop_frontNoEx(); switch (evt->type) { case CET_NICK_REMOVE: m_nicks.remove(((ChatEventNick *)evt)->nick); break; case CET_NICK_ADD: m_nicks.push_back(((ChatEventNick *)evt)->nick); break; case CET_CHAT: complete_redraw_needed = true; // This is only used for direct replies from commands // or for lua's print() functionality m_chat_backend.addMessage(L"", ((ChatEventChat *)evt)->evt_msg); break; case CET_TIME_INFO: ChatEventTimeInfo *tevt = (ChatEventTimeInfo *)evt; m_game_time = tevt->game_time; m_time_of_day = tevt->time; }; delete evt; } while (!m_log_output.queue.empty()) { complete_redraw_needed = true; std::pair<LogLevel, std::string> p = m_log_output.queue.pop_frontNoEx(); if (p.first > m_log_level) continue; m_chat_backend.addMessage( utf8_to_wide(Logger::getLevelLabel(p.first)), utf8_to_wide(p.second)); } // handle input if (!m_esc_mode) { handleInput(ch, complete_redraw_needed); } else { switch (ch) { case ERR: // no input break; case 27: // ESC // Toggle ESC mode m_esc_mode = !m_esc_mode; break; case 'L': m_log_level--; m_log_level = MYMAX(m_log_level, LL_NONE + 1); // LL_NONE isn't accessible break; case 'l': m_log_level++; m_log_level = MYMIN(m_log_level, LL_MAX - 1); break; } } // was there a resize? int xn, yn; getmaxyx(stdscr, yn, xn); if (xn != m_cols || yn != m_rows) { m_cols = xn; m_rows = yn; m_can_draw_text = reformat_backend(&m_chat_backend, m_rows, m_cols); complete_redraw_needed = true; } // draw title move(0, 0); clrtoeol(); addstr(PROJECT_NAME_C); addstr(" "); addstr(g_version_hash); u32 minutes = m_time_of_day % 1000; u32 hours = m_time_of_day / 1000; minutes = (float)minutes / 1000 * 60; if (m_game_time) printw(" | Game %d Time of day %02d:%02d ", m_game_time, hours, minutes); // draw text if (complete_redraw_needed && m_can_draw_text) draw_text(); // draw prompt if (!m_esc_mode) { // normal prompt ChatPrompt& prompt = m_chat_backend.getPrompt(); std::string prompt_text = wide_to_utf8(prompt.getVisiblePortion()); move(m_rows - 1, 0); clrtoeol(); addstr(prompt_text.c_str()); // Draw cursor s32 cursor_pos = prompt.getVisibleCursorPosition(); if (cursor_pos >= 0) { move(m_rows - 1, cursor_pos); } } else { // esc prompt move(m_rows - 1, 0); clrtoeol(); printw("[ESC] Toggle ESC mode |" " [CTRL+C] Shut down |" " (L) in-, (l) decrease loglevel %s", Logger::getLevelLabel((LogLevel) m_log_level).c_str()); } refresh(); } void TerminalChatConsole::draw_text() { ChatBuffer& buf = m_chat_backend.getConsoleBuffer(); for (u32 row = 0; row < buf.getRows(); row++) { move_for_backend(row, 0); clrtoeol(); const ChatFormattedLine& line = buf.getFormattedLine(row); if (line.fragments.empty()) continue; for (u32 i = 0; i < line.fragments.size(); ++i) { const ChatFormattedFragment& fragment = line.fragments[i]; addstr(wide_to_utf8(fragment.text).c_str()); } } } void TerminalChatConsole::stopAndWaitforThread() { clearKillStatus(); stop(); wait(); } #endif