GLPostProcessor.cc 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595
  1. #include "GLPostProcessor.hh"
  2. #include "GLContext.hh"
  3. #include "GLScaler.hh"
  4. #include "GLScalerFactory.hh"
  5. #include "FloatSetting.hh"
  6. #include "OutputSurface.hh"
  7. #include "RawFrame.hh"
  8. #include "Math.hh"
  9. #include "InitException.hh"
  10. #include "gl_transform.hh"
  11. #include "random.hh"
  12. #include "ranges.hh"
  13. #include "stl.hh"
  14. #include "vla.hh"
  15. #include <cassert>
  16. #include <cstdint>
  17. using namespace gl;
  18. namespace openmsx {
  19. GLPostProcessor::GLPostProcessor(
  20. MSXMotherBoard& motherBoard_, Display& display_,
  21. OutputSurface& screen_, const std::string& videoSource,
  22. unsigned maxWidth_, unsigned height_, bool canDoInterlace_)
  23. : PostProcessor(motherBoard_, display_, screen_,
  24. videoSource, maxWidth_, height_, canDoInterlace_)
  25. , noiseTextureA(true, true) // interpolate + wrap
  26. , noiseTextureB(true, true)
  27. , height(height_)
  28. {
  29. scaleAlgorithm = static_cast<RenderSettings::ScaleAlgorithm>(-1); // not a valid scaler
  30. frameCounter = 0;
  31. noiseX = noiseY = 0.0f;
  32. preCalcNoise(renderSettings.getNoise());
  33. initBuffers();
  34. storedFrame = false;
  35. for (int i = 0; i < 2; ++i) {
  36. colorTex[i].bind();
  37. colorTex[i].setInterpolation(true);
  38. auto [w, h] = screen.getLogicalSize();
  39. glTexImage2D(GL_TEXTURE_2D, // target
  40. 0, // level
  41. GL_RGB8, // internal format
  42. w, // width
  43. h, // height
  44. 0, // border
  45. GL_RGB, // format
  46. GL_UNSIGNED_BYTE, // type
  47. nullptr); // data
  48. fbo[i] = FrameBufferObject(colorTex[i]);
  49. }
  50. VertexShader vertexShader ("monitor3D.vert");
  51. FragmentShader fragmentShader("monitor3D.frag");
  52. monitor3DProg.attach(vertexShader);
  53. monitor3DProg.attach(fragmentShader);
  54. monitor3DProg.bindAttribLocation(0, "a_position");
  55. monitor3DProg.bindAttribLocation(1, "a_normal");
  56. monitor3DProg.bindAttribLocation(2, "a_texCoord");
  57. monitor3DProg.link();
  58. preCalcMonitor3D(renderSettings.getHorizontalStretch());
  59. renderSettings.getNoiseSetting().attach(*this);
  60. renderSettings.getHorizontalStretchSetting().attach(*this);
  61. }
  62. GLPostProcessor::~GLPostProcessor()
  63. {
  64. renderSettings.getHorizontalStretchSetting().detach(*this);
  65. renderSettings.getNoiseSetting().detach(*this);
  66. }
  67. void GLPostProcessor::initBuffers()
  68. {
  69. const vec2 pos[4] = {
  70. vec2(-1, 1), vec2(-1,-1), vec2( 1,-1), vec2( 1, 1)
  71. };
  72. const vec2 tex[4] = {
  73. vec2( 0, 1), vec2( 0, 0), vec2( 1, 0), vec2( 1, 1)
  74. };
  75. glBindBuffer(GL_ARRAY_BUFFER, vbo[0].get());
  76. glBufferData(GL_ARRAY_BUFFER, sizeof(pos), pos, GL_STATIC_DRAW);
  77. glBindBuffer(GL_ARRAY_BUFFER, vbo[1].get());
  78. glBufferData(GL_ARRAY_BUFFER, sizeof(tex), tex, GL_STATIC_DRAW);
  79. vao.bind();
  80. glBindBuffer(GL_ARRAY_BUFFER, vbo[0].get());
  81. glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
  82. glEnableVertexAttribArray(0);
  83. glBindBuffer(GL_ARRAY_BUFFER, vbo[1].get());
  84. glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
  85. glEnableVertexAttribArray(1);
  86. vao.unbind();
  87. glowVAO.bind();
  88. glBindBuffer(GL_ARRAY_BUFFER, vbo[0].get());
  89. glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
  90. glEnableVertexAttribArray(0);
  91. glBindBuffer(GL_ARRAY_BUFFER, vbo[1].get());
  92. glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
  93. glEnableVertexAttribArray(1);
  94. glowVAO.unbind();
  95. }
  96. void GLPostProcessor::createRegions()
  97. {
  98. regions.clear();
  99. const unsigned srcHeight = paintFrame->getHeight();
  100. const unsigned dstHeight = screen.getLogicalHeight();
  101. unsigned g = Math::gcd(srcHeight, dstHeight);
  102. unsigned srcStep = srcHeight / g;
  103. unsigned dstStep = dstHeight / g;
  104. // TODO: Store all MSX lines in RawFrame and only scale the ones that fit
  105. // on the PC screen, as a preparation for resizable output window.
  106. unsigned srcStartY = 0;
  107. unsigned dstStartY = 0;
  108. while (dstStartY < dstHeight) {
  109. // Currently this is true because the source frame height
  110. // is always >= dstHeight/(dstStep/srcStep).
  111. assert(srcStartY < srcHeight);
  112. // get region with equal lineWidth
  113. unsigned lineWidth = getLineWidth(paintFrame, srcStartY, srcStep);
  114. unsigned srcEndY = srcStartY + srcStep;
  115. unsigned dstEndY = dstStartY + dstStep;
  116. while ((srcEndY < srcHeight) && (dstEndY < dstHeight) &&
  117. (getLineWidth(paintFrame, srcEndY, srcStep) == lineWidth)) {
  118. srcEndY += srcStep;
  119. dstEndY += dstStep;
  120. }
  121. regions.emplace_back(srcStartY, srcEndY,
  122. dstStartY, dstEndY,
  123. lineWidth);
  124. // next region
  125. srcStartY = srcEndY;
  126. dstStartY = dstEndY;
  127. }
  128. }
  129. void GLPostProcessor::paint(OutputSurface& /*output*/)
  130. {
  131. if (renderSettings.getInterleaveBlackFrame()) {
  132. interleaveCount ^= 1;
  133. if (interleaveCount) {
  134. glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
  135. glClear(GL_COLOR_BUFFER_BIT);
  136. return;
  137. }
  138. }
  139. auto deform = renderSettings.getDisplayDeform();
  140. float horStretch = renderSettings.getHorizontalStretch();
  141. int glow = renderSettings.getGlow();
  142. bool renderToTexture = (deform != RenderSettings::DEFORM_NORMAL) ||
  143. (horStretch != 320.0f) ||
  144. (glow != 0) ||
  145. screen.isViewScaled();
  146. if ((screen.getViewOffset() != ivec2()) || // any part of the screen not covered by the viewport?
  147. (deform == RenderSettings::DEFORM_3D) || !paintFrame) {
  148. glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
  149. glClear(GL_COLOR_BUFFER_BIT);
  150. if (!paintFrame) {
  151. return;
  152. }
  153. }
  154. // New scaler algorithm selected?
  155. auto algo = renderSettings.getScaleAlgorithm();
  156. if (scaleAlgorithm != algo) {
  157. scaleAlgorithm = algo;
  158. currScaler = GLScalerFactory::createScaler(renderSettings);
  159. // Re-upload frame data, this is both
  160. // - Chunks of RawFrame with a specific linewidth, possibly
  161. // with some extra lines above and below each chunk that are
  162. // also converted to this linewidth.
  163. // - Extra data that is specific for the scaler (ATM only the
  164. // hq and hqlite scalers require this).
  165. // Re-uploading the first is not strictly needed. But switching
  166. // scalers doesn't happen that often, so it also doesn't hurt
  167. // and it keeps the code simpler.
  168. uploadFrame();
  169. }
  170. auto [scrnWidth, scrnHeight] = screen.getLogicalSize();
  171. if (renderToTexture) {
  172. glViewport(0, 0, scrnWidth, scrnHeight);
  173. glBindTexture(GL_TEXTURE_2D, 0);
  174. fbo[frameCounter & 1].push();
  175. }
  176. glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
  177. for (auto& r : regions) {
  178. //fprintf(stderr, "post processing lines %d-%d: %d\n",
  179. // r.srcStartY, r.srcEndY, r.lineWidth);
  180. auto it = find_if_unguarded(textures,
  181. EqualTupleValue<0>(r.lineWidth));
  182. auto superImpose = superImposeVideoFrame
  183. ? &superImposeTex : nullptr;
  184. currScaler->scaleImage(
  185. it->second.tex, superImpose,
  186. r.srcStartY, r.srcEndY, r.lineWidth, // src
  187. r.dstStartY, r.dstEndY, scrnWidth, // dst
  188. paintFrame->getHeight()); // dst
  189. //GLUtil::checkGLError("GLPostProcessor::paint");
  190. }
  191. drawNoise();
  192. drawGlow(glow);
  193. if (renderToTexture) {
  194. fbo[frameCounter & 1].pop();
  195. colorTex[frameCounter & 1].bind();
  196. auto [x, y] = screen.getViewOffset();
  197. auto [w, h] = screen.getViewSize();
  198. glViewport(x, y, w, h);
  199. if (deform == RenderSettings::DEFORM_3D) {
  200. drawMonitor3D();
  201. } else {
  202. gl::context->progTex.activate();
  203. glUniform4f(gl::context->unifTexColor,
  204. 1.0f, 1.0f, 1.0f, 1.0f);
  205. mat4 I;
  206. glUniformMatrix4fv(gl::context->unifTexMvp,
  207. 1, GL_FALSE, &I[0][0]);
  208. vao.bind();
  209. if (horStretch != 320.0f) {
  210. float x1 = (320.0f - float(horStretch)) / (2.0f * 320.0f);
  211. float x2 = 1.0f - x1;
  212. vec2 tex[4] = {
  213. vec2(x1, 1), vec2(x1, 0), vec2(x2, 0), vec2(x2, 1)
  214. };
  215. glBindBuffer(GL_ARRAY_BUFFER, stretchVBO.get());
  216. glBufferData(GL_ARRAY_BUFFER, sizeof(tex), tex, GL_STATIC_DRAW);
  217. } else {
  218. glBindBuffer(GL_ARRAY_BUFFER, vbo[1].get());
  219. }
  220. glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
  221. glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
  222. vao.unbind();
  223. }
  224. storedFrame = true;
  225. } else {
  226. storedFrame = false;
  227. }
  228. }
  229. std::unique_ptr<RawFrame> GLPostProcessor::rotateFrames(
  230. std::unique_ptr<RawFrame> finishedFrame, EmuTime::param time)
  231. {
  232. std::unique_ptr<RawFrame> reuseFrame =
  233. PostProcessor::rotateFrames(std::move(finishedFrame), time);
  234. uploadFrame();
  235. ++frameCounter;
  236. noiseX = random_float(0.0f, 1.0f);
  237. noiseY = random_float(0.0f, 1.0f);
  238. return reuseFrame;
  239. }
  240. void GLPostProcessor::update(const Setting& setting)
  241. {
  242. VideoLayer::update(setting);
  243. auto& noiseSetting = renderSettings.getNoiseSetting();
  244. auto& horizontalStretch = renderSettings.getHorizontalStretchSetting();
  245. if (&setting == &noiseSetting) {
  246. preCalcNoise(noiseSetting.getDouble());
  247. } else if (&setting == &horizontalStretch) {
  248. preCalcMonitor3D(horizontalStretch.getDouble());
  249. }
  250. }
  251. void GLPostProcessor::uploadFrame()
  252. {
  253. createRegions();
  254. const unsigned srcHeight = paintFrame->getHeight();
  255. for (auto& r : regions) {
  256. // upload data
  257. // TODO get before/after data from scaler
  258. unsigned before = 1;
  259. unsigned after = 1;
  260. uploadBlock(std::max<int>(0, r.srcStartY - before),
  261. std::min<int>(srcHeight, r.srcEndY + after),
  262. r.lineWidth);
  263. }
  264. if (superImposeVideoFrame) {
  265. int w = superImposeVideoFrame->getWidth();
  266. int h = superImposeVideoFrame->getHeight();
  267. if (superImposeTex.getWidth() != w ||
  268. superImposeTex.getHeight() != h) {
  269. superImposeTex.resize(w, h);
  270. superImposeTex.setInterpolation(true);
  271. }
  272. superImposeTex.bind();
  273. glTexSubImage2D(
  274. GL_TEXTURE_2D, // target
  275. 0, // level
  276. 0, // offset x
  277. 0, // offset y
  278. w, // width
  279. h, // height
  280. GL_BGRA, // format
  281. GL_UNSIGNED_BYTE, // type
  282. const_cast<RawFrame*>(superImposeVideoFrame)->getLinePtrDirect<unsigned>(0)); // data
  283. }
  284. }
  285. void GLPostProcessor::uploadBlock(
  286. unsigned srcStartY, unsigned srcEndY, unsigned lineWidth)
  287. {
  288. // create texture/pbo if needed
  289. auto it = ranges::find_if(textures, EqualTupleValue<0>(lineWidth));
  290. if (it == end(textures)) {
  291. TextureData textureData;
  292. textureData.tex.resize(lineWidth, height * 2); // *2 for interlace
  293. textureData.pbo.setImage(lineWidth, height * 2);
  294. textures.emplace_back(lineWidth, std::move(textureData));
  295. it = end(textures) - 1;
  296. }
  297. auto& tex = it->second.tex;
  298. auto& pbo = it->second.pbo;
  299. // bind texture
  300. tex.bind();
  301. // upload data
  302. uint32_t* mapped;
  303. pbo.bind();
  304. mapped = pbo.mapWrite();
  305. for (unsigned y = srcStartY; y < srcEndY; ++y) {
  306. auto* dest = mapped + y * lineWidth;
  307. auto* data = paintFrame->getLinePtr(y, lineWidth, dest);
  308. if (data != dest) {
  309. memcpy(dest, data, lineWidth * sizeof(uint32_t));
  310. }
  311. }
  312. pbo.unmap();
  313. #if defined(__APPLE__)
  314. // The nVidia GL driver for the GeForce 8000/9000 series seems to hang
  315. // on texture data replacements that are 1 pixel wide and start on a
  316. // line number that is a non-zero multiple of 16.
  317. if (lineWidth == 1 && srcStartY != 0 && srcStartY % 16 == 0) {
  318. srcStartY--;
  319. }
  320. #endif
  321. glTexSubImage2D(
  322. GL_TEXTURE_2D, // target
  323. 0, // level
  324. 0, // offset x
  325. srcStartY, // offset y
  326. lineWidth, // width
  327. srcEndY - srcStartY, // height
  328. GL_BGRA, // format
  329. GL_UNSIGNED_BYTE, // type
  330. pbo.getOffset(0, srcStartY)); // data
  331. pbo.unbind();
  332. // possibly upload scaler specific data
  333. if (currScaler) {
  334. currScaler->uploadBlock(srcStartY, srcEndY, lineWidth, *paintFrame);
  335. }
  336. }
  337. void GLPostProcessor::drawGlow(int glow)
  338. {
  339. if ((glow == 0) || !storedFrame) return;
  340. gl::context->progTex.activate();
  341. glEnable(GL_BLEND);
  342. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  343. colorTex[(frameCounter & 1) ^ 1].bind();
  344. glUniform4f(gl::context->unifTexColor,
  345. 1.0f, 1.0f, 1.0f, glow * 31 / 3200.0f);
  346. mat4 I;
  347. glUniformMatrix4fv(gl::context->unifTexMvp, 1, GL_FALSE, &I[0][0]);
  348. glowVAO.bind();
  349. glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
  350. glowVAO.unbind();
  351. glDisable(GL_BLEND);
  352. }
  353. void GLPostProcessor::preCalcNoise(float factor)
  354. {
  355. GLbyte buf1[256 * 256];
  356. GLbyte buf2[256 * 256];
  357. auto& generator = global_urng(); // fast (non-cryptographic) random numbers
  358. std::normal_distribution<float> distribution(0.0f, 1.0f);
  359. for (int i = 0; i < 256 * 256; ++i) {
  360. float r = distribution(generator);
  361. int s = Math::clip<-255, 255>(roundf(r * factor));
  362. buf1[i] = (s > 0) ? s : 0;
  363. buf2[i] = (s < 0) ? -s : 0;
  364. }
  365. noiseTextureA.bind();
  366. glTexImage2D(
  367. GL_TEXTURE_2D, // target
  368. 0, // level
  369. GL_RED, // internal format
  370. 256, // width
  371. 256, // height
  372. 0, // border
  373. GL_RED, // format
  374. GL_UNSIGNED_BYTE, // type
  375. buf1); // data
  376. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_RED);
  377. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED);
  378. noiseTextureB.bind();
  379. glTexImage2D(
  380. GL_TEXTURE_2D, // target
  381. 0, // level
  382. GL_RED, // internal format
  383. 256, // width
  384. 256, // height
  385. 0, // border
  386. GL_RED, // format
  387. GL_UNSIGNED_BYTE, // type
  388. buf2); // data
  389. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_RED);
  390. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED);
  391. const vec2 pos[8][4] = {
  392. { { -1, -1 }, { 1, -1 }, { 1, 1 }, { -1, 1 } },
  393. { { -1, 1 }, { 1, 1 }, { 1, -1 }, { -1, -1 } },
  394. { { -1, 1 }, { -1, -1 }, { 1, -1 }, { 1, 1 } },
  395. { { 1, 1 }, { 1, -1 }, { -1, -1 }, { -1, 1 } },
  396. { { 1, 1 }, { -1, 1 }, { -1, -1 }, { 1, -1 } },
  397. { { 1, -1 }, { -1, -1 }, { -1, 1 }, { 1, 1 } },
  398. { { 1, -1 }, { 1, 1 }, { -1, 1 }, { -1, -1 } },
  399. { { -1, -1 }, { -1, 1 }, { 1, 1 }, { 1, -1 } }
  400. };
  401. glBindBuffer(GL_ARRAY_BUFFER, noiseVBO[0].get());
  402. glBufferData(GL_ARRAY_BUFFER, sizeof(pos), pos, GL_STATIC_DRAW);
  403. }
  404. void GLPostProcessor::drawNoise()
  405. {
  406. if (renderSettings.getNoise() == 0.0f) return;
  407. // Rotate and mirror noise texture in consecutive frames to avoid
  408. // seeing 'patterns' in the noise.
  409. vec2 noise(noiseX, noiseY);
  410. const vec2 tex[4] = {
  411. noise + vec2(0.0f, 1.875f),
  412. noise + vec2(2.0f, 1.875f),
  413. noise + vec2(2.0f, 0.0f ),
  414. noise + vec2(0.0f, 0.0f )
  415. };
  416. unsigned seq = frameCounter & 7;
  417. gl::context->progTex.activate();
  418. glEnable(GL_BLEND);
  419. glBlendFunc(GL_ONE, GL_ONE);
  420. glUniform4f(gl::context->unifTexColor, 1.0f, 1.0f, 1.0f, 1.0f);
  421. mat4 I;
  422. glUniformMatrix4fv(gl::context->unifTexMvp, 1, GL_FALSE, &I[0][0]);
  423. noiseVAO.bind();
  424. glBindBuffer(GL_ARRAY_BUFFER, noiseVBO[0].get());
  425. vec2* base = nullptr;
  426. glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, base + seq * 4);
  427. glEnableVertexAttribArray(0);
  428. glBindBuffer(GL_ARRAY_BUFFER, noiseVBO[1].get());
  429. glBufferData(GL_ARRAY_BUFFER, sizeof(tex), tex, GL_STATIC_DRAW);
  430. glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
  431. glEnableVertexAttribArray(1);
  432. noiseTextureA.bind();
  433. glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
  434. glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
  435. noiseTextureB.bind();
  436. glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
  437. noiseVAO.unbind();
  438. glBlendEquation(GL_FUNC_ADD); // restore default
  439. glDisable(GL_BLEND);
  440. }
  441. constexpr int GRID_SIZE = 16;
  442. constexpr int GRID_SIZE1 = GRID_SIZE + 1;
  443. constexpr int NUM_INDICES = (GRID_SIZE1 * 2 + 2) * GRID_SIZE - 2;
  444. struct Vertex {
  445. vec3 position;
  446. vec3 normal;
  447. vec2 tex;
  448. };
  449. void GLPostProcessor::preCalcMonitor3D(float width)
  450. {
  451. // precalculate vertex-positions, -normals and -texture-coordinates
  452. Vertex vertices[GRID_SIZE1][GRID_SIZE1];
  453. constexpr float GRID_SIZE2 = float(GRID_SIZE) / 2.0f;
  454. float s = width / 320.0f;
  455. float b = (320.0f - width) / (2.0f * 320.0f);
  456. for (int sx = 0; sx < GRID_SIZE1; ++sx) {
  457. for (int sy = 0; sy < GRID_SIZE1; ++sy) {
  458. Vertex& v = vertices[sx][sy];
  459. float x = (sx - GRID_SIZE2) / GRID_SIZE2;
  460. float y = (sy - GRID_SIZE2) / GRID_SIZE2;
  461. v.position = vec3(x, y, (x * x + y * y) / -12.0f);
  462. v.normal = normalize(vec3(x / 6.0f, y / 6.0f, 1.0f)) * 1.2f;
  463. v.tex = vec2((float(sx) / GRID_SIZE) * s + b,
  464. float(sy) / GRID_SIZE);
  465. }
  466. }
  467. // calculate indices
  468. uint16_t indices[NUM_INDICES];
  469. uint16_t* ind = indices;
  470. for (int y = 0; y < GRID_SIZE; ++y) {
  471. for (int x = 0; x < GRID_SIZE1; ++x) {
  472. *ind++ = (y + 0) * GRID_SIZE1 + x;
  473. *ind++ = (y + 1) * GRID_SIZE1 + x;
  474. }
  475. // skip 2, filled in later
  476. ind += 2;
  477. }
  478. assert((ind - indices) == NUM_INDICES + 2);
  479. ind = indices;
  480. for (int y = 0; y < (GRID_SIZE - 1); ++y) {
  481. ind += 2 * GRID_SIZE1;
  482. // repeat prev and next index to restart strip
  483. ind[0] = ind[-1];
  484. ind[1] = ind[ 2];
  485. ind += 2;
  486. }
  487. // upload calculated values to buffers
  488. monitor3DVAO.bind();
  489. glBindBuffer(GL_ARRAY_BUFFER, arrayBuffer.get());
  490. glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices,
  491. GL_STATIC_DRAW);
  492. char* base = nullptr;
  493. glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex),
  494. base);
  495. glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex),
  496. base + sizeof(vec3));
  497. glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex),
  498. base + sizeof(vec3) + sizeof(vec3));
  499. glEnableVertexAttribArray(0);
  500. glEnableVertexAttribArray(1);
  501. glEnableVertexAttribArray(2);
  502. glBindBuffer(GL_ARRAY_BUFFER, 0);
  503. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementbuffer.get());
  504. glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices,
  505. GL_STATIC_DRAW);
  506. monitor3DVAO.unbind();
  507. // calculate transformation matrices
  508. mat4 proj = frustum(-1, 1, -1, 1, 1, 10);
  509. mat4 tran = translate(vec3(0.0f, 0.4f, -2.0f));
  510. mat4 rotx = rotateX(radians(-10.0f));
  511. mat4 scal = scale(vec3(2.2f, 2.2f, 2.2f));
  512. mat3 normal = mat3(rotx);
  513. mat4 mvp = proj * tran * rotx * scal;
  514. // set uniforms
  515. monitor3DProg.activate();
  516. glUniform1i(monitor3DProg.getUniformLocation("u_tex"), 0);
  517. glUniformMatrix4fv(monitor3DProg.getUniformLocation("u_mvpMatrix"),
  518. 1, GL_FALSE, &mvp[0][0]);
  519. glUniformMatrix3fv(monitor3DProg.getUniformLocation("u_normalMatrix"),
  520. 1, GL_FALSE, &normal[0][0]);
  521. }
  522. void GLPostProcessor::drawMonitor3D()
  523. {
  524. monitor3DProg.activate();
  525. monitor3DVAO.bind();
  526. glDrawElements(GL_TRIANGLE_STRIP, NUM_INDICES, GL_UNSIGNED_SHORT, nullptr);
  527. monitor3DVAO.unbind();
  528. }
  529. } // namespace openmsx