<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Smbat Voskanyan</title><link>https://senpie.net/</link><description>Recent content on Smbat Voskanyan</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Thu, 22 Feb 2024 00:00:00 +0000</lastBuildDate><atom:link href="https://senpie.net/index.xml" rel="self" type="application/rss+xml"/><item><title>Resume</title><link>https://senpie.net/page/resume/</link><pubDate>Thu, 22 Feb 2024 00:00:00 +0000</pubDate><guid>https://senpie.net/page/resume/</guid><description>&lt;script type="text/javascript" src= '/js/pdf-js/build/pdf.js'>&lt;/script>
&lt;style>
#embed-pdf-container {
position: relative;
width: 100%;
height: auto;
min-height: 20vh;
}
.pdf-canvas {
border: 1px solid black;
direction: ltr;
width: 100%;
height: auto;
display: none;
}
#the-canvas {
border: 1px solid black;
direction: ltr;
width: 100%;
height: auto;
display: none;
}
.pdf-loadingWrapper {
display: none;
justify-content: center;
align-items: center;
width: 100%;
height: 350px;
}
.pdf-loading {
display: inline-block;
width: 50px;
height: 50px;
border: 3px solid #d2d0d0;;
border-radius: 50%;
border-top-color: #383838;
animation: spin 1s ease-in-out infinite;
-webkit-animation: spin 1s ease-in-out infinite;
}
#overlayText {
word-wrap: break-word;
display: grid;
justify-content: end;
}
#overlayText a {
position: relative;
top: 10px;
right: 4px;
color: #000;
margin: auto;
background-color: #eeeeee;
padding: 0.3em 1em;
border: solid 2px;
border-radius: 12px;
border-color: #00000030;
text-decoration: none;
}
#overlayText svg {
height: clamp(1em, 2vw, 1.4em);
width: clamp(1em, 2vw, 1.4em);
}
@keyframes spin {
to { -webkit-transform: rotate(360deg); }
}
@-webkit-keyframes spin {
to { -webkit-transform: rotate(360deg); }
}
&lt;/style>&lt;div class="embed-pdf-container" id="embed-pdf-container-7f03bf2b">
&lt;div class="pdf-loadingWrapper" id="pdf-loadingWrapper-7f03bf2b">
&lt;div class="pdf-loading" id="pdf-loading-7f03bf2b">&lt;/div>
&lt;/div>
&lt;div id="overlayText">
&lt;a href="resume.pdf" aria-label="Download" download>
&lt;svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18">
&lt;path d="M9 13c.3 0 .5-.1.7-.3L15.4 7 14 5.6l-4 4V1H8v8.6l-4-4L2.6 7l5.7 5.7c.2.2.4.3.7.3zm-7 2h14v2H2z" />
&lt;/svg>
&lt;/a>
&lt;/div>
&lt;canvas class="pdf-canvas" id="pdf-canvas-7f03bf2b">&lt;/canvas>
&lt;/div>
&lt;div class="pdf-paginator" id="pdf-paginator-7f03bf2b">
&lt;button id="pdf-prev-7f03bf2b">Previous&lt;/button>
&lt;button id="pdf-next-7f03bf2b">Next&lt;/button> &amp;nbsp; &amp;nbsp;
&lt;span>
&lt;span class="pdf-pagenum" id="pdf-pagenum-7f03bf2b">&lt;/span> / &lt;span class="pdf-pagecount" id="pdf-pagecount-7f03bf2b">&lt;/span>
&lt;/span>
&lt;a class="pdf-source" id="pdf-source-7f03bf2b" href="resume.pdf">[pdf]&lt;/a>
&lt;/div>
&lt;noscript>
View the PDF file &lt;a class="pdf-source" id="pdf-source-noscript-7f03bf2b" href="resume.pdf">here&lt;/a>.
&lt;/noscript>
&lt;script type="text/javascript">
(function(){
var url = 'resume.pdf';
var hidePaginator = "true" === "true";
var hideLoader = "" === "true";
var selectedPageNum = parseInt("") || 1;
var pdfjsLib = window['pdfjs-dist/build/pdf'];
if (pdfjsLib.GlobalWorkerOptions.workerSrc == '')
pdfjsLib.GlobalWorkerOptions.workerSrc = "https:\/\/senpie.net\/" + 'js/pdf-js/build/pdf.worker.js';
var pdfDoc = null,
pageNum = selectedPageNum,
pageRendering = false,
pageNumPending = null,
scale = 3,
canvas = document.getElementById('pdf-canvas-7f03bf2b'),
ctx = canvas.getContext('2d'),
paginator = document.getElementById("pdf-paginator-7f03bf2b"),
loadingWrapper = document.getElementById('pdf-loadingWrapper-7f03bf2b');
showPaginator();
showLoader();
function renderPage(num) {
pageRendering = true;
pdfDoc.getPage(num).then(function(page) {
var viewport = page.getViewport({scale: scale});
canvas.height = viewport.height;
canvas.width = viewport.width;
var renderContext = {
canvasContext: ctx,
viewport: viewport
};
var renderTask = page.render(renderContext);
renderTask.promise.then(function() {
pageRendering = false;
showContent();
if (pageNumPending !== null) {
renderPage(pageNumPending);
pageNumPending = null;
}
});
});
document.getElementById('pdf-pagenum-7f03bf2b').textContent = num;
}
function showContent() {
loadingWrapper.style.display = 'none';
canvas.style.display = 'block';
}
function showLoader() {
if(hideLoader) return
loadingWrapper.style.display = 'flex';
canvas.style.display = 'none';
}
function showPaginator() {
if(hidePaginator) return
paginator.style.display = 'block';
}
function queueRenderPage(num) {
if (pageRendering) {
pageNumPending = num;
} else {
renderPage(num);
}
}
function onPrevPage() {
if (pageNum &lt;= 1) {
return;
}
pageNum--;
queueRenderPage(pageNum);
}
document.getElementById('pdf-prev-7f03bf2b').addEventListener('click', onPrevPage);
function onNextPage() {
if (pageNum >= pdfDoc.numPages) {
return;
}
pageNum++;
queueRenderPage(pageNum);
}
document.getElementById('pdf-next-7f03bf2b').addEventListener('click', onNextPage);
pdfjsLib.getDocument(url).promise.then(function(pdfDoc_) {
pdfDoc = pdfDoc_;
var numPages = pdfDoc.numPages;
document.getElementById('pdf-pagecount-7f03bf2b').textContent = numPages;
if(pageNum > numPages) {
pageNum = numPages
}
renderPage(pageNum);
});
})();
&lt;/script></description></item><item><title>Tiny Ray Tracer</title><link>https://senpie.net/post/tiny-ray-tracer/</link><pubDate>Tue, 20 Feb 2024 00:00:00 +0000</pubDate><guid>https://senpie.net/post/tiny-ray-tracer/</guid><description>&lt;img src="https://senpie-portfolio.s3.eu-north-1.amazonaws.com/image+-+robo+fight+high+res.png" alt="Featured image of post Tiny Ray Tracer" />&lt;p>I have a background in programming, including a keen interest in graphics and game engine development. Inspired by the &amp;ldquo;Raytracing in One Weekend&amp;rdquo; book series and Jacco Bikker&amp;rsquo;s insightful blog on ray tracing optimizations, I set out to build my own ray tracer. My goal was to create a feature-rich ray tracer with a focus on performance. The source code is avaible on &lt;strong>&lt;a class="link" href="https://github.com/TheSenPie/tiny-ray-tracer" target="_blank" rel="noopener"
>&lt;del>my github page&lt;/del>&lt;/a>&lt;/strong>. The rendered results are in the &lt;strong>&lt;a class="link" href="https://github.com/TheSenPie/tiny-ray-tracer/tree/main/renders" target="_blank" rel="noopener"
>/renders&lt;/a>&lt;/strong> folder.&lt;/p>
&lt;h1 id="foundational-features">Foundational Features
&lt;/h1>&lt;h2 id="scene-creation-and-camera">Scene Creation and Camera
&lt;/h2>&lt;p>To start, I implemented camera controls with &amp;ldquo;look-at&amp;rdquo; functionality. This allowed me to position the camera flexibly within a scene and adjust the image framing. I worked on defining basic materials: diffuse for matte surfaces, metal with adjustable fuzziness, and transparent glass. This enabled me to create scenes with a variety of visual effects.&lt;/p>
&lt;h2 id="image-refinement">Image Refinement
&lt;/h2>&lt;p>Anti-aliasing was crucial for reducing jagged edges and creating smoother renders. I also tackled issues like shadow acne, where incorrect shadow calculations lead to visual artifacts.&lt;/p>
&lt;p>A final render from this part of the journey, which took ~12 minutes to render. Enjoy!&lt;/p>
&lt;p>&lt;img src="https://senpie-portfolio.s3.eu-north-1.amazonaws.com/image&amp;#43;-&amp;#43;four&amp;#43;spheres.png"
loading="lazy"
alt="Final render"
>&lt;/p>
&lt;h1 id="the-power-of-optimization">The Power of Optimization
&lt;/h1>&lt;h2 id="early-multi-threading-success">Early Multi-Threading Success
&lt;/h2>&lt;p>I began my performance work with multi-threading. Since ray tracing is inherently parallelizable (rays are independent calculations), this yielded immediate speedups.&lt;/p>
&lt;h2 id="with-great-models-comes-great-amount-of-primitives">With great models comes great amount of primitives
&lt;/h2>&lt;p>To make my scenes more interesting it was time to add support for triangles and models. For ray triangle intersection I implemented class &lt;a class="link" href="https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm" target="_blank" rel="noopener"
>Möller–Trumbore&lt;/a> intersection algorithm, which is still pretty close to &amp;lsquo;as fast as possible&amp;rsquo;. Next, I hooked up &lt;a class="link" href="https://en.wikipedia.org/wiki/Open_Asset_Import_Library" target="_blank" rel="noopener"
>Assimp&lt;/a> for loading .obj &lt;a class="link" href="https://en.wikipedia.org/wiki/Wavefront_.obj_file" target="_blank" rel="noopener"
>wavefront&lt;/a> files with their diffuse materials.&lt;/p>
&lt;p>&lt;img src="https://senpie.net/post/tiny-ray-tracer/triangle-cube.png"
width="400"
height="225"
srcset="https://senpie.net/post/tiny-ray-tracer/triangle-cube_hu7b6ca5810fff4bcb4d5806fe217b3b91_178389_480x0_resize_box_3.png 480w, https://senpie.net/post/tiny-ray-tracer/triangle-cube_hu7b6ca5810fff4bcb4d5806fe217b3b91_178389_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Triangle and a cube"
class="gallery-image"
data-flex-grow="177"
data-flex-basis="426px"
> &lt;img src="https://senpie.net/post/tiny-ray-tracer/robot.png"
width="1000"
height="1000"
srcset="https://senpie.net/post/tiny-ray-tracer/robot_hu792b822dd187085686db23f634a041d5_1196699_480x0_resize_box_3.png 480w, https://senpie.net/post/tiny-ray-tracer/robot_hu792b822dd187085686db23f634a041d5_1196699_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Robot"
class="gallery-image"
data-flex-grow="100"
data-flex-basis="240px"
> &lt;img src="https://senpie.net/post/tiny-ray-tracer/cow.png"
width="400"
height="225"
srcset="https://senpie.net/post/tiny-ray-tracer/cow_hu385a6bb33d2471e7b519aff23f3e873d_40992_480x0_resize_box_3.png 480w, https://senpie.net/post/tiny-ray-tracer/cow_hu385a6bb33d2471e7b519aff23f3e873d_40992_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Cow"
class="gallery-image"
data-flex-grow="177"
data-flex-basis="426px"
>&lt;/p>
&lt;h2 id="the-heart-of-acceleration-bounding-volume-hierarchies">The Heart of Acceleration: Bounding Volume Hierarchies
&lt;/h2>&lt;p>Since I had the models it was time to optimize, so I focused on implementing BVHs &lt;a class="link" href="https://en.wikipedia.org/wiki/Bounding_volume_hierarchy" target="_blank" rel="noopener"
>Bounding volume hierarchy&lt;/a>. The &lt;a class="link" href="https://jacco.ompf2.com/2022/04/13/how-to-build-a-bvh-part-1-basics/" target="_blank" rel="noopener"
>article series&lt;/a> by Jacco Bikker were a great help for implementing fast BVHs. The performance gains here were even more drastic.&lt;/p>
&lt;blockquote>
&lt;p>4882 Spheres Scene&lt;br>
 *No BVH: 52936ms&lt;br>
 *Simple BVH: 896.871ms&lt;br>
 *Simple Jacco BVH: 1391.54ms&lt;br>
 *SAH Jacco BVH: 1321.69ms&lt;br>
 *SAH Jacco Iterative BVH: 725.51ms&lt;br>&lt;/p>
&lt;p>11102 Triangles Dragon Model&lt;br>
 *No BVH: 250936ms&lt;br>
 *Simple BVH: 3904.27ms&lt;br>
 *Simple Jacco BVH: 1522.3ms&lt;br>
 *SAH Jacco BVH: 1298.32ms&lt;br>
 *SAH Jacco Iterative BVH: 974.8ms&lt;br>&lt;/p>
&lt;/blockquote>
&lt;p>From numbers you can see around &lt;strong>70 times&lt;/strong> faster render time for spheres scene and mind-blowing &lt;strong>250 times&lt;/strong> faster rendering on dragon scene.&lt;/p>
&lt;h1 id="lessons-learned">Lessons Learned
&lt;/h1>&lt;h2 id="performance-is-king">Performance is King
&lt;/h2>&lt;p>The BVH optimizations were a turning point, demonstrating the significant impact of efficient algorithms and data structures on rendering speed. There are other dirty optimizations, that I have on my to-do list, but I left them for later to keep the code readability simple for now.&lt;/p>
&lt;h2 id="debugging-tales">Debugging Tales
&lt;/h2>&lt;h3 id="funny-visual-glitches">Funny Visual Glitches
&lt;/h3>&lt;p>I encountered some annoying/amusing visual errors where models would render with parts inverted or cut off. Both issues stemmed from returning from intersection searches too early. Thorough testing with simplified models and debugging sessions resolved these.&lt;/p>
&lt;p>&lt;img src="https://senpie.net/post/tiny-ray-tracer/bvh-bug-1.png"
width="400"
height="225"
srcset="https://senpie.net/post/tiny-ray-tracer/bvh-bug-1_hu78407c20f4871a9e4124b1645eff2d3d_32220_480x0_resize_box_3.png 480w, https://senpie.net/post/tiny-ray-tracer/bvh-bug-1_hu78407c20f4871a9e4124b1645eff2d3d_32220_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Sphere cut off by BVH"
class="gallery-image"
data-flex-grow="177"
data-flex-basis="426px"
> &lt;img src="https://senpie.net/post/tiny-ray-tracer/bvh-bug-2.png"
width="400"
height="225"
srcset="https://senpie.net/post/tiny-ray-tracer/bvh-bug-2_huc65b52f5f0475a03ede104a1b549bcd2_76963_480x0_resize_box_3.png 480w, https://senpie.net/post/tiny-ray-tracer/bvh-bug-2_huc65b52f5f0475a03ede104a1b549bcd2_76963_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Triangle BVHs all cutting each other"
class="gallery-image"
data-flex-grow="177"
data-flex-basis="426px"
> &lt;img src="https://senpie.net/post/tiny-ray-tracer/box-bug.png"
width="400"
height="225"
srcset="https://senpie.net/post/tiny-ray-tracer/box-bug_hu2012dbea3c8b68c168a8e84f568abc8f_181089_480x0_resize_box_3.png 480w, https://senpie.net/post/tiny-ray-tracer/box-bug_hu2012dbea3c8b68c168a8e84f568abc8f_181089_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Rendering wrong faces"
class="gallery-image"
data-flex-grow="177"
data-flex-basis="426px"
>&lt;/p>
&lt;h3 id="the-missing-volume">The Missing Volume
&lt;/h3>&lt;p>A numerical bug caused triangle bounding boxes to become flat, preventing ray intersections. After much head-scratching, I solved it with minor padding, similar to a solution mentioned in Peter Shirley&amp;rsquo;s book.&lt;/p>
&lt;h1 id="updates-scaling-up-and-optimizing-further">Updates: Scaling Up and Optimizing Further
&lt;/h1>&lt;p>After implementing BVHs based on Jacco&amp;rsquo;s series, I successfully rendered a single robot. But who doesn&amp;rsquo;t love a robot army? Scaling up to 256 robots demanded an extra layer of optimization to manage this scene complexity.&lt;/p>
&lt;p>Here&amp;rsquo;s what I added:&lt;/p>
&lt;p>&lt;strong>TLAS (Top-Level Acceleration Structure):&lt;/strong> TLASes act like a &amp;lsquo;manager&amp;rsquo; over multiple BVHs. They use agglomerative clustering, a bottom-up approach that makes building the hierarchy faster. This is important when dealing with lots of individual models.&lt;/p>
&lt;p>&lt;strong>BVH Instances:&lt;/strong> These provide a flexible way to wrap my existing BVHs in a way that allows me to easily transform (translate, rotate, scale) the models. This let me position my robot army without having to rebuild the BVH for each robot.&lt;/p>
&lt;p>As a result we have a significant speedup, allowing me to efficiently render a scene containing hundreds of complex models.&lt;/p>
&lt;blockquote>
&lt;p>I present you 256 robots in rotation.
&lt;img src="https://senpie-portfolio.s3.eu-north-1.amazonaws.com/image&amp;#43;-&amp;#43;256&amp;#43;robots&amp;#43;2.png"
loading="lazy"
alt="256 robots"
>&lt;/p>
&lt;/blockquote>
&lt;h1 id="putting-it-all-together-lights-camera-render">Putting it all Together: Lights, Camera, Render!
&lt;/h1>&lt;p>Since I had all the pieces of the puzzle, it was only natural to put them together. With the ability to render spheres, complex models, and multiples of them efficiently, it was time to build a compelling scene. However, a truly eye-catching scene requires well-placed lighting. Let&amp;rsquo;s dive into how I implemented emissive materials.&lt;/p>
&lt;h2 id="emissive-materials-letting-robots-shine">Emissive Materials: Letting Robots Shine
&lt;/h2>&lt;p>To give my robots a striking appearance. I introduced an emissive material type. It works by biasing how light rays scatter, making certain surfaces appear to emit light. The implementation was straightforward, but the results transformed the look of the scene.&lt;/p>
&lt;p>&lt;img src="https://senpie.net/post/tiny-ray-tracer/simple-lights.png"
width="640"
height="360"
srcset="https://senpie.net/post/tiny-ray-tracer/simple-lights_hu04d3a24d1c207d24007cddddc2665741_261004_480x0_resize_box_3.png 480w, https://senpie.net/post/tiny-ray-tracer/simple-lights_hu04d3a24d1c207d24007cddddc2665741_261004_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Emissive sphere"
class="gallery-image"
data-flex-grow="177"
data-flex-basis="426px"
> &lt;img src="https://senpie.net/post/tiny-ray-tracer/radioactive-cube.png"
width="400"
height="225"
srcset="https://senpie.net/post/tiny-ray-tracer/radioactive-cube_hu20d220064fbf8b4b106fb0940399ac68_28322_480x0_resize_box_3.png 480w, https://senpie.net/post/tiny-ray-tracer/radioactive-cube_hu20d220064fbf8b4b106fb0940399ac68_28322_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Emissive cube"
class="gallery-image"
data-flex-grow="177"
data-flex-basis="426px"
>&lt;/p>
&lt;p>This addition laid the foundation for creative lighting design. With everything in place, I&amp;rsquo;m eager to share the final scene in my next update!&lt;/p>
&lt;h2 id="final-renders-when-visions-shift-and-robots-party">Final Renders: When Visions Shift and Robots Party
&lt;/h2>&lt;h2 id="the-epic-and-unexpectedly-funny-first-attempt">The Epic (and Unexpectedly Funny) First Attempt
&lt;/h2>&lt;p>My first idea was to make this scene I had in mind for a couple of weeks. Sun surrounded with EVA-01 robots from the Evangelion series. There were many tiny spheres which surrounded the scene and looked like particles from the sun. In my mind, I though it was the most epic shit, however the results turned out rather funny.&lt;/p>
&lt;p>&lt;img src="https://senpie-portfolio.s3.eu-north-1.amazonaws.com/image&amp;#43;-&amp;#43;eva&amp;#43;sun&amp;#43;high&amp;#43;res.png"
loading="lazy"
alt="Eva fight sun scene"
>&lt;/p>
&lt;p>One of my friends sent me this meme when he saw the render, which made it even funnier.&lt;/p>
&lt;p>&lt;img src="https://senpie.net/post/tiny-ray-tracer/lion-sun-meme.png"
width="599"
height="383"
srcset="https://senpie.net/post/tiny-ray-tracer/lion-sun-meme_hudcd7f381416a8c485b0e61241542ee0a_175115_480x0_resize_box_3.png 480w, https://senpie.net/post/tiny-ray-tracer/lion-sun-meme_hudcd7f381416a8c485b0e61241542ee0a_175115_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Who would win, sun or one trillion lions?"
class="gallery-image"
data-flex-grow="156"
data-flex-basis="375px"
>&lt;/p>
&lt;h2 id="pivoting-to-awesome-the-robot-party-is-born">Pivoting to Awesome: The Robot Party is Born
&lt;/h2>&lt;p>No time to play! I still wanted to make a cool-looking render, so I reimagined the whole scene to look like a cool robot party with a bunch of emissive spheres and a light beacon in the middle. I think the results turned out super cool!&lt;/p>
&lt;p>&lt;img src="https://senpie-portfolio.s3.eu-north-1.amazonaws.com/image&amp;#43;-&amp;#43;robo&amp;#43;fight&amp;#43;high&amp;#43;res.png"
loading="lazy"
alt="Final render"
>&lt;/p>
&lt;h2 id="lessons-learned-1">Lessons Learned
&lt;/h2>&lt;p>This whole experience taught me a couple of things:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>The Unexpected is Part of the Fun:&lt;/strong> Sometimes, the best things come out of left field. A little humor and a not-so-perfect first render can make the process even more enjoyable.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Don&amp;rsquo;t be Afraid to Iterate:&lt;/strong> If something isn&amp;rsquo;t working, toss it and start again! There&amp;rsquo;s no shame in changing direction to find something truly awesome.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Visuals Matter (and Robots are Cool):&lt;/strong> A great final render is its own reward. Plus, ray tracing can make robots look absolutely epic (even when they&amp;rsquo;re partying).&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>I hope you enjoyed this peek into my ray tracer project – from epic fails to robot raves!&lt;/strong>&lt;/p>
&lt;h1 id="thanks">Thanks
&lt;/h1>&lt;p>First, I wanted to thank &lt;strong>Peter @Peter_shirley Shirley&lt;/strong> and &lt;strong>Jacco @j_bikker Bikker&lt;/strong> for putting such high-quality content out there for free, for people like me to be inspired and thrive to improve our skills in computer graphics.&lt;/p>
&lt;p>Secondly, I want to thank my friends and brilliant game developers &lt;strong>Andranik @andranik3949 Melikyan&lt;/strong> and &lt;strong>Bagrat @dBagrat Dabaghyan&lt;/strong> for helping me out when I got stuck with stupid bugs and getting me out of burnout state!&lt;/p>
&lt;h1 id="next-steps">Next Steps:
&lt;/h1>&lt;p>&lt;strong>Lighting Expansion:&lt;/strong> Update: Completed! Implementing emissive lights will allow for more realistic and nuanced lighting effects.&lt;br>
&lt;strong>Better Model Support:&lt;/strong> Add support for Normal Mapping, Metalness and Roughness maps. Although, for later other stuff should be refactored too.
&lt;strong>Advanced Sampling:&lt;/strong> I&amp;rsquo;ll explore monte carlo algorithms for smoother images and reduced visual artifacts and other probabilistic models for predicting properties of light scattering in scene.&lt;br>
&lt;strong>GPU Acceleration:&lt;/strong> Re-implementing my path tracer in CUDA will allow for faster render times and the ability to tackle even more complex scenes.&lt;br>&lt;/p></description></item><item><title>Talos VFX</title><link>https://senpie.net/post/talos-vfx/</link><pubDate>Sun, 23 Jul 2023 00:00:00 +0000</pubDate><guid>https://senpie.net/post/talos-vfx/</guid><description>&lt;img src="https://senpie.net/post/talos-vfx/talos.png" alt="Featured image of post Talos VFX" />&lt;h1 id="introduction">Introduction
&lt;/h1>&lt;p>Talos VFX is a powerful open-source visual effects editor designed for Java and LibGDX projects. Create stunning particles, shaders, and custom scripts, while seamlessly managing game assets – all from a single workspace. Its intuitive, node-based VFX editor and support for visual programming routines (similar to Unreal Engine&amp;rsquo;s blueprints) empower you to craft dynamic effects. Build 2D levels and scenes with brushes, and bring your creations to life with Spine2d skeletal animations, easily attaching VFX directly to animation bones. While primarily focused on 2D, Talos VFX also offers limited 3D support.&lt;/p>
&lt;p>As a major contributor to Talos VFX, I&amp;rsquo;ve played a role in squashing critical bugs, implementing user-requested features, and refining the overall user experience. As of writing this post I authored around ~50% of Pull Requests on the project. Here&amp;rsquo;s the &lt;strong>&lt;a class="link" href="https://github.com/rockbite/talos/tree/talos-3d" target="_blank" rel="noopener"
>link to project&lt;/a>&lt;/strong>. Note, that active development is carried on &lt;code>talos-3d&lt;/code> branch and &lt;code>master&lt;/code> branch is greatly behind.&lt;/p>
&lt;p>&lt;img src="https://senpie.net/post/talos-vfx/talos.png"
width="2131"
height="1276"
srcset="https://senpie.net/post/talos-vfx/talos_hu9ab9ff59ef09db64b97121e1afd1e09a_1006924_480x0_resize_box_3.png 480w, https://senpie.net/post/talos-vfx/talos_hu9ab9ff59ef09db64b97121e1afd1e09a_1006924_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="On the top-left scene hierarchy, preview and scene editor. On the bottom-left routine graphical editor. On the right node-based particle system. "
class="gallery-image"
data-flex-grow="167"
data-flex-basis="400px"
>&lt;/p>
&lt;h1 id="feature-development">Feature Development
&lt;/h1>&lt;p>Since a lot of features were missing initally and we needed many tools for our prototypes, I had a chance to contribute a lot in this area.&lt;/p>
&lt;h2 id="fake-bvb-bone-attachment-support">Fake BVB&amp;quot; (Bone Attachment Support)
&lt;/h2>&lt;p>Problem: Users were limited in how they could interact with skeletal animations, hindering dynamic effects and customization.&lt;br>
Solution: Implemented &amp;ldquo;Fake BVB&amp;rdquo; functionality, exposing animation bones as restricted game objects within Talos, enabling the attachment of other objects.&lt;br>
Result: Users can now create more complex and visually engaging effects by directly linking particles, objects, and more to
skeletal animations. Examples include attaching characters to moving vehicles or syncing particle effects to bone movements.&lt;/p>
&lt;h2 id="nineslices-support-and-sprite-editor">NineSlices Support and Sprite Editor
&lt;/h2>&lt;p>Problem: Users lacked advanced scaling options for UI and graphic elements, limiting design flexibility. Limited or inefficient tools for sprite editing and managing NineSlice metadata within Talos VFX.&lt;br>
Solution: Implemented NineSlice rendering functionality, providing a system similar to Unity&amp;rsquo;s NineSlices. This allows specific sections of images to be repeated horizontally, vertically, or in both directions.&lt;br>
Result: Improved control over how UI elements and graphics scale, enhancing the ability to create visually polished and responsive interfaces within Talos VFX.&lt;br>&lt;/p>
&lt;h1 id="bug-fixes-and-refinements">Bug Fixes and Refinements
&lt;/h1>&lt;p>I have fixed more bugs than I could remember, however to recall some off the top of my head, then these would be the ones.&lt;/p>
&lt;h2 id="project-explorer-stability-and-user-experience">Project Explorer Stability and User Experience
&lt;/h2>&lt;p>Problem: File management was unreliable, with crashes and unexpected behaviors. Crashes when moving or renaming files. Bad user experience. Poor file search performance.&lt;br>
Solution: Revamped project explorer logic, preventing errors and ensuring a smoother file management experience. Refreshed the UI for more costumization and assets previews. Fast asset lookup.&lt;br>
Result: Increased stability and reliability within Talos VFX, protecting user work. Happy users.&lt;br>&lt;/p>
&lt;h2 id="routine-workflow">Routine Workflow
&lt;/h2>&lt;p>Problem: Editing curves within the Routine App was buggy, hindering intuitive workflow.&lt;br>
Solution: Fixed curve gizmo display and interaction points, including inserting new points and using the ESC key.&lt;br>
Result: Restored smooth and predictable curve editing, streamlining the creation of dynamic effects within routines.&lt;br>&lt;/p>
&lt;h1 id="concusion">Concusion
&lt;/h1>&lt;p>Through this project, I&amp;rsquo;ve learned many new topics and significantly improved my knowledge in Java, libGDX, and 2D engine development tools. It was both challenging and rewarding to work with a community of people directly affected by my contributions. Seeing others use Talos VFX to create amazing games is a deeply satisfying experience.&lt;/p></description></item><item><title>Tiny World</title><link>https://senpie.net/post/tiny-world/</link><pubDate>Thu, 05 Jan 2023 00:00:00 +0000</pubDate><guid>https://senpie.net/post/tiny-world/</guid><description>&lt;img src="https://senpie.net/post/tiny-world/capture.jpg" alt="Featured image of post Tiny World" />&lt;div class="video-wrapper">
&lt;iframe loading="lazy"
src="https://www.youtube.com/embed/hA-29bpJ4Bc"
allowfullscreen
title="YouTube Video"
>
&lt;/iframe>
&lt;/div>
&lt;p>I&amp;rsquo;ve created a game engine from scratch featuring a tiny
world. This project aimed to demonstrate my understanding of 3D graphics programming and the
principles of perspective, lighting, and texturing. I had a lot of fun implementing the water. It features Fresnel Effect, samples skybox and terrain for realistic reflections and uses depth buffer information to achieve soft edges.
I wrote the project purely in Java and OpenGL, and the source code is available on &lt;a class="link" href="https://github.com/TheSenPie/tiny-world-java" target="_blank" rel="noopener"
>git&lt;/a>.&lt;/p>
&lt;p>Furthermore, this is a personal live project meaning it will be constantly updated with new stuff as I keep
learning new techniques and would want to practically apply them somewhere. Specifics and more details
about the project, and how to run it on your local machine are uploaded to the webpage provided in the
link. Enjoy!&lt;/p>
&lt;p>&lt;strong>&lt;a class="link" href="https://github.com/TheSenPie/tiny-world-java" target="_blank" rel="noopener"
>Source code&lt;/a>&lt;/strong> for curious ones.&lt;/p></description></item><item><title>AI Algorithms Practice</title><link>https://senpie.net/post/ai/</link><pubDate>Thu, 22 Dec 2022 00:00:00 +0000</pubDate><guid>https://senpie.net/post/ai/</guid><description>&lt;img src="https://images.inc.com/uploaded_files/image/1920x1080/getty_484267200_109172.jpg" alt="Featured image of post AI Algorithms Practice" />&lt;p>I took a bonus assignment for my Artificial Intelligence course. The work demonstrates the implementation of various algorithms for solving the n-puzzle problem, along with useful statistics for analysis. Due to the professor’s request, I have made the repository private, so students of upcoming years will not copy it, but you can access the code with &lt;strong>&lt;a class="link" href="https://gitfront.io/r/SenPie/fhSwHm6Huxwx/ai-22/" target="_blank" rel="noopener"
>this link&lt;/a>&lt;/strong>.&lt;/p>
&lt;h2 id="statistics">Statistics
&lt;/h2>&lt;p>Part 1&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">Executed Breadth First Tree Search
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Nodes Generated 2298274; Max Nodes in frontier 1473871
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Executed Breadth First Tree Search with Early Goal test
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Nodes Generated 824404; Max Nodes in frontier 537119
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Executed Breadth First Graph Search
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Nodes Generated 5019; Max Nodes in frontier 1926
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Executed Breadth First Graph Search with Early Goal test
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Nodes Generated 4644; Max Nodes in frontier 1075
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Skipped the execution of Depth First Tree Search
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Algorithm gets into an infinite cycle of Right &amp;lt;-&amp;gt; Left actions
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Executed Depth First Graph Search
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Nodes Generated 68217; Max Nodes in frontier 29633
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Executed Iterative Deepening Tree Search
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Nodes Generated 34232; Max Nodes in frontier 43
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Both Breadth First Tree and Graph Searches found the optimal solution. Clearly huge improvements are observed with graph search version and the early goal test gives the extra bonus of cutting off extra nodes, that would be generated otherwise.&lt;br>
Although, Depth First Tree Search is known to consume very little memory space it is not effective in this case as it gets into an infinite loop.&lt;br>
Depth First Graph Search does a better job, than its Tree Search version xD. However, it generates many more nodes, than Breadth First Graph search and has a bigger frontier stretch.
Iterative Deepening Tree Search did a good job to find a solution, and it also spent very little memory. However, it is also worth to mention, that IDTS did not find the optimal solution. Also still, 4644 + 1075 &amp;lt; 34232 + 43 it still consumes more memory. You may argue, that compared to BFGS, IDTS does not use an extra set to keep track of &lt;em>reached&lt;/em> states, however reached states is not really making new states, but rather keeps references, which is still much less, than what we get with IDTS.&lt;br>
Overall, Breadth First Graph Search with the early goal test comes out as a winner in uninformed search category, reason being, that it just works better for this problem.&lt;br>
As it says in the book&lt;/p>
&lt;blockquote>
&lt;p>When all step costs are equal, breadth-first search is optimal because it always expands the shallowest unexpanded node.&lt;/p>
&lt;/blockquote>
&lt;p>Part 2&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">Executed Uniform Cost Tree Search
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Nodes Generated 2587508; Max Nodes in frontier 1653452
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Executed Uniform Cost Graph Search
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Nodes Generated 7549; Max Nodes in frontier 1656
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Skipped the execution of Greedy Tree Search with h1 number of misplaced tiles
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Algorithm gets into an infinite cycle.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Skipped the execution of Greedy Tree Search with h2 manhattan distance
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Algorithm gets into an infinite cycle.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Executed Greedy Graph Search with h1 number of misplaced tiles
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Nodes Generated 233; Max Nodes in frontier 68
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Executed Greedy Graph Search with h2 manhattan distance
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Nodes Generated 1051; Max Nodes in frontier 267
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Executed A* Tree Search with h1, number of misplaced tiles Tree Search
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Nodes Generated 964; Max Nodes in frontier 613
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Executed A* Tree Search with h2 manhattan distance Tree Search
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Nodes Generated 120; Max Nodes in frontier 74
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Executed A* Graph Search with h1 number of misplaced tiles
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Nodes Generated 360; Max Nodes in frontier 91
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Executed A* Graph Search with h2 manhattan distance
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Nodes Generated 106; Max Nodes in frontier 29
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Before we continue any further, I would like to right away give the trophy to A* Graph Search with Manhattan Distance heuristic.&lt;br>
Let&amp;rsquo;s jump into it! Right away we can see that Uniform Cost Tree Search gave similar results to Breadth First Tree Search. Reason is that action cost is 1, and path costs gets incrementally bigger with each level forcing UCS to iterate in Breadth first manner. Reason why numbers are not same is connected to the fact how JDK implemented the PriorityQueue, which means it is not guaranteed to return the leftmost node in the same level.&lt;br>
Same goes for Uniform Cost Graph Search and Breadth First Graph Search.&lt;br>
Neither Number of Misplaced Tiles heuristic, nor Manhattan Distance heuristic are good enough on their own to run Tree Search, therefore Greedy Tree search gets into a cycle.
Again quoting the book:&lt;/p>
&lt;blockquote>
&lt;p>Algorithms that don&amp;rsquo;t remember the past are doomed to repeat the history.&lt;/p>
&lt;/blockquote>
&lt;p>Regarding A* Search we see power of good heuristic, because A* Tree Search with Manhattan distance performs better than A* Graph Search with Number of Misplaced Tiles heuristic. It is proved in the book that &lt;strong>h2&lt;/strong> &lt;em>dominates&lt;/em> &lt;strong>h1&lt;/strong>.&lt;br>
It also has lower &lt;em>Effective Branching Factor&lt;/em>.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>d&lt;/th>
&lt;th>BFS&lt;/th>
&lt;th>A*(h1)&lt;/th>
&lt;th>A*(h2)&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>6&lt;/td>
&lt;td>2.01&lt;/td>
&lt;td>1.42&lt;/td>
&lt;td>1.34&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>8&lt;/td>
&lt;td>1.91&lt;/td>
&lt;td>1.40&lt;/td>
&lt;td>1.30&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>10&lt;/td>
&lt;td>1.85&lt;/td>
&lt;td>1.43&lt;/td>
&lt;td>1.27&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>12&lt;/td>
&lt;td>1.80&lt;/td>
&lt;td>1.45&lt;/td>
&lt;td>1.28&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>14&lt;/td>
&lt;td>1.77&lt;/td>
&lt;td>1.47&lt;/td>
&lt;td>1.31&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>16&lt;/td>
&lt;td>1.74&lt;/td>
&lt;td>1.48&lt;/td>
&lt;td>1.32&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>18&lt;/td>
&lt;td>1.72&lt;/td>
&lt;td>1.49&lt;/td>
&lt;td>1.34&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>20&lt;/td>
&lt;td>1.69&lt;/td>
&lt;td>1.50&lt;/td>
&lt;td>1.34&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>22&lt;/td>
&lt;td>1.66&lt;/td>
&lt;td>1.50&lt;/td>
&lt;td>1.34&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>24&lt;/td>
&lt;td>1.62&lt;/td>
&lt;td>1.50&lt;/td>
&lt;td>1.36&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Overall, obviously and for the reasons mentioned before, A* Graph Search with Manhattan Distance heuristic comes top as a winner.&lt;/p>
&lt;h2 id="solution">Solution
&lt;/h2>&lt;p>Following is a demonstration of A* Graph Search on 8-puzzle with Manhattan Distance heuristics.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt"> 10
&lt;/span>&lt;span class="lnt"> 11
&lt;/span>&lt;span class="lnt"> 12
&lt;/span>&lt;span class="lnt"> 13
&lt;/span>&lt;span class="lnt"> 14
&lt;/span>&lt;span class="lnt"> 15
&lt;/span>&lt;span class="lnt"> 16
&lt;/span>&lt;span class="lnt"> 17
&lt;/span>&lt;span class="lnt"> 18
&lt;/span>&lt;span class="lnt"> 19
&lt;/span>&lt;span class="lnt"> 20
&lt;/span>&lt;span class="lnt"> 21
&lt;/span>&lt;span class="lnt"> 22
&lt;/span>&lt;span class="lnt"> 23
&lt;/span>&lt;span class="lnt"> 24
&lt;/span>&lt;span class="lnt"> 25
&lt;/span>&lt;span class="lnt"> 26
&lt;/span>&lt;span class="lnt"> 27
&lt;/span>&lt;span class="lnt"> 28
&lt;/span>&lt;span class="lnt"> 29
&lt;/span>&lt;span class="lnt"> 30
&lt;/span>&lt;span class="lnt"> 31
&lt;/span>&lt;span class="lnt"> 32
&lt;/span>&lt;span class="lnt"> 33
&lt;/span>&lt;span class="lnt"> 34
&lt;/span>&lt;span class="lnt"> 35
&lt;/span>&lt;span class="lnt"> 36
&lt;/span>&lt;span class="lnt"> 37
&lt;/span>&lt;span class="lnt"> 38
&lt;/span>&lt;span class="lnt"> 39
&lt;/span>&lt;span class="lnt"> 40
&lt;/span>&lt;span class="lnt"> 41
&lt;/span>&lt;span class="lnt"> 42
&lt;/span>&lt;span class="lnt"> 43
&lt;/span>&lt;span class="lnt"> 44
&lt;/span>&lt;span class="lnt"> 45
&lt;/span>&lt;span class="lnt"> 46
&lt;/span>&lt;span class="lnt"> 47
&lt;/span>&lt;span class="lnt"> 48
&lt;/span>&lt;span class="lnt"> 49
&lt;/span>&lt;span class="lnt"> 50
&lt;/span>&lt;span class="lnt"> 51
&lt;/span>&lt;span class="lnt"> 52
&lt;/span>&lt;span class="lnt"> 53
&lt;/span>&lt;span class="lnt"> 54
&lt;/span>&lt;span class="lnt"> 55
&lt;/span>&lt;span class="lnt"> 56
&lt;/span>&lt;span class="lnt"> 57
&lt;/span>&lt;span class="lnt"> 58
&lt;/span>&lt;span class="lnt"> 59
&lt;/span>&lt;span class="lnt"> 60
&lt;/span>&lt;span class="lnt"> 61
&lt;/span>&lt;span class="lnt"> 62
&lt;/span>&lt;span class="lnt"> 63
&lt;/span>&lt;span class="lnt"> 64
&lt;/span>&lt;span class="lnt"> 65
&lt;/span>&lt;span class="lnt"> 66
&lt;/span>&lt;span class="lnt"> 67
&lt;/span>&lt;span class="lnt"> 68
&lt;/span>&lt;span class="lnt"> 69
&lt;/span>&lt;span class="lnt"> 70
&lt;/span>&lt;span class="lnt"> 71
&lt;/span>&lt;span class="lnt"> 72
&lt;/span>&lt;span class="lnt"> 73
&lt;/span>&lt;span class="lnt"> 74
&lt;/span>&lt;span class="lnt"> 75
&lt;/span>&lt;span class="lnt"> 76
&lt;/span>&lt;span class="lnt"> 77
&lt;/span>&lt;span class="lnt"> 78
&lt;/span>&lt;span class="lnt"> 79
&lt;/span>&lt;span class="lnt"> 80
&lt;/span>&lt;span class="lnt"> 81
&lt;/span>&lt;span class="lnt"> 82
&lt;/span>&lt;span class="lnt"> 83
&lt;/span>&lt;span class="lnt"> 84
&lt;/span>&lt;span class="lnt"> 85
&lt;/span>&lt;span class="lnt"> 86
&lt;/span>&lt;span class="lnt"> 87
&lt;/span>&lt;span class="lnt"> 88
&lt;/span>&lt;span class="lnt"> 89
&lt;/span>&lt;span class="lnt"> 90
&lt;/span>&lt;span class="lnt"> 91
&lt;/span>&lt;span class="lnt"> 92
&lt;/span>&lt;span class="lnt"> 93
&lt;/span>&lt;span class="lnt"> 94
&lt;/span>&lt;span class="lnt"> 95
&lt;/span>&lt;span class="lnt"> 96
&lt;/span>&lt;span class="lnt"> 97
&lt;/span>&lt;span class="lnt"> 98
&lt;/span>&lt;span class="lnt"> 99
&lt;/span>&lt;span class="lnt">100
&lt;/span>&lt;span class="lnt">101
&lt;/span>&lt;span class="lnt">102
&lt;/span>&lt;span class="lnt">103
&lt;/span>&lt;span class="lnt">104
&lt;/span>&lt;span class="lnt">105
&lt;/span>&lt;span class="lnt">106
&lt;/span>&lt;span class="lnt">107
&lt;/span>&lt;span class="lnt">108
&lt;/span>&lt;span class="lnt">109
&lt;/span>&lt;span class="lnt">110
&lt;/span>&lt;span class="lnt">111
&lt;/span>&lt;span class="lnt">112
&lt;/span>&lt;span class="lnt">113
&lt;/span>&lt;span class="lnt">114
&lt;/span>&lt;span class="lnt">115
&lt;/span>&lt;span class="lnt">116
&lt;/span>&lt;span class="lnt">117
&lt;/span>&lt;span class="lnt">118
&lt;/span>&lt;span class="lnt">119
&lt;/span>&lt;span class="lnt">120
&lt;/span>&lt;span class="lnt">121
&lt;/span>&lt;span class="lnt">122
&lt;/span>&lt;span class="lnt">123
&lt;/span>&lt;span class="lnt">124
&lt;/span>&lt;span class="lnt">125
&lt;/span>&lt;span class="lnt">126
&lt;/span>&lt;span class="lnt">127
&lt;/span>&lt;span class="lnt">128
&lt;/span>&lt;span class="lnt">129
&lt;/span>&lt;span class="lnt">130
&lt;/span>&lt;span class="lnt">131
&lt;/span>&lt;span class="lnt">132
&lt;/span>&lt;span class="lnt">133
&lt;/span>&lt;span class="lnt">134
&lt;/span>&lt;span class="lnt">135
&lt;/span>&lt;span class="lnt">136
&lt;/span>&lt;span class="lnt">137
&lt;/span>&lt;span class="lnt">138
&lt;/span>&lt;span class="lnt">139
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">0: start
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| 7 | 4 | 2 |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| 8 | 1 | 3 |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| 5 | | 6 |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">1: move LEFT
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| 7 | 4 | 2 |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| 8 | 1 | 3 |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| | 5 | 6 |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2: move UP
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| 7 | 4 | 2 |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| | 1 | 3 |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| 8 | 5 | 6 |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">3: move UP
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| | 4 | 2 |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| 7 | 1 | 3 |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| 8 | 5 | 6 |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">4: move RIGHT
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| 4 | | 2 |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| 7 | 1 | 3 |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| 8 | 5 | 6 |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">5: move DOWN
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| 4 | 1 | 2 |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| 7 | | 3 |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| 8 | 5 | 6 |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">6: move DOWN
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| 4 | 1 | 2 |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| 7 | 5 | 3 |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| 8 | | 6 |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">7: move LEFT
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| 4 | 1 | 2 |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| 7 | 5 | 3 |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| | 8 | 6 |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">8: move UP
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| 4 | 1 | 2 |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| | 5 | 3 |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| 7 | 8 | 6 |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">9: move UP
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| | 1 | 2 |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| 4 | 5 | 3 |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| 7 | 8 | 6 |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">10: move RIGHT
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| 1 | | 2 |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| 4 | 5 | 3 |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| 7 | 8 | 6 |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">11: move RIGHT
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| 1 | 2 | |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| 4 | 5 | 3 |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| 7 | 8 | 6 |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">12: move DOWN
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| 1 | 2 | 3 |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| 4 | 5 | |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| 7 | 8 | 6 |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">13: move DOWN
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| 1 | 2 | 3 |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| 4 | 5 | 6 |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| 7 | 8 | |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div></description></item><item><title>Battle Cards: Real-time PVP</title><link>https://senpie.net/post/battle-cards/</link><pubDate>Fri, 25 Feb 2022 00:00:00 +0000</pubDate><guid>https://senpie.net/post/battle-cards/</guid><description>&lt;img src="https://senpie.net/post/battle-cards/battlecards-bg.png" alt="Featured image of post Battle Cards: Real-time PVP" />&lt;p>Check out &lt;strong>&lt;a class="link" href="https://play.google.com/store/apps/details?id=com.rockbite.battlecards" target="_blank" rel="noopener"
>Battle Cards&lt;/a>&lt;/strong>! It was an incredible journey that began at the &lt;strong>&lt;a class="link" href="https://globalgamejam.org/2021/games/realtime-battlecards-9" target="_blank" rel="noopener"
>Playcrafting NYC Game Jam 2021&lt;/a>&lt;/strong>. Our team managed to create a compelling multiplayer prototype in just two days, earning recognition from the judges. Built with Java, LibGDX, and a Node.js backend, we had a solid foundation for a full release. Seeing the positive response, we decided to take &amp;ldquo;Battle Cards&amp;rdquo; to the next level. After a year of dedicated development, we launched a polished version that players genuinely enjoyed.&lt;/p>
&lt;p>&lt;img src="https://senpie.net/post/battle-cards/battle-cards-0.jpeg"
width="590"
height="1080"
srcset="https://senpie.net/post/battle-cards/battle-cards-0_hueb5e5c24123d5ee684c61a5d1c6a1652_397805_480x0_resize_q75_box.jpeg 480w, https://senpie.net/post/battle-cards/battle-cards-0_hueb5e5c24123d5ee684c61a5d1c6a1652_397805_1024x0_resize_q75_box.jpeg 1024w"
loading="lazy"
alt="Main page for searching a match"
class="gallery-image"
data-flex-grow="54"
data-flex-basis="131px"
>&lt;img src="https://senpie.net/post/battle-cards/battle-cards-1.jpeg"
width="592"
height="1080"
srcset="https://senpie.net/post/battle-cards/battle-cards-1_hu11c7ab14234c447f9c89cbc62e465f79_523041_480x0_resize_q75_box.jpeg 480w, https://senpie.net/post/battle-cards/battle-cards-1_hu11c7ab14234c447f9c89cbc62e465f79_523041_1024x0_resize_q75_box.jpeg 1024w"
loading="lazy"
alt="The battle arena"
class="gallery-image"
data-flex-grow="54"
data-flex-basis="131px"
>&lt;img src="https://senpie.net/post/battle-cards/battle-cards-2.jpeg"
width="592"
height="1080"
srcset="https://senpie.net/post/battle-cards/battle-cards-2_hu5fe3390afd1012ea7c16ac4c639de51e_645366_480x0_resize_q75_box.jpeg 480w, https://senpie.net/post/battle-cards/battle-cards-2_hu5fe3390afd1012ea7c16ac4c639de51e_645366_1024x0_resize_q75_box.jpeg 1024w"
loading="lazy"
alt="Card collection"
class="gallery-image"
data-flex-grow="54"
data-flex-basis="131px"
>&lt;/p>
&lt;p>My contributions centered on the UI, ensuring new features, code refactoring, and optimizations all worked toward a seamless player experience. However, being part of a small team meant I got to wear many hats! I contributed to gameplay programming, designed some eye-catching VFX, and tweaked the backend for better performance. For our marketing efforts, I created a compact 2MB playable ad to showcase the heart of the game.&lt;/p>
&lt;p>Additionally, I collaborated closely with our analytics team to improve dashboards, graphs, filters, and other tools. Our goal was to make analyzing user experience more accessible, enabling us to make informed game design changes. Ultimately, my focus was on making sure &amp;ldquo;Battle Cards&amp;rdquo; didn&amp;rsquo;t just look good – it had to play well too. It was a challenging, yet incredibly rewarding project.&lt;/p></description></item><item><title>Sandship: Crafting Factory</title><link>https://senpie.net/post/sandship/</link><pubDate>Mon, 15 Feb 2021 00:00:00 +0000</pubDate><guid>https://senpie.net/post/sandship/</guid><description>&lt;img src="https://senpie.net/post/sandship/sandship-bg.png" alt="Featured image of post Sandship: Crafting Factory" />&lt;p>Explore the world of &lt;strong>&lt;a class="link" href="https://play.google.com/store/apps/details?id=com.rockbite.sandship" target="_blank" rel="noopener"
>Sandship&lt;/a>&lt;/strong> blends the vastness of a Dune-like world with the addictive mechanics of Factorio. It was one of my first projects at Rockbite Games and offered an array of exciting technical challenges. I played a crucial role in crafting the game&amp;rsquo;s UI, implementing new elements, extensively refactoring for efficiency, and squashing bugs. Java and LibGDX were my tools of choice.&lt;/p>
&lt;div class="video-wrapper">
&lt;iframe loading="lazy"
src="https://www.youtube.com/embed/QR5Kn37fHyY"
allowfullscreen
title="YouTube Video"
>
&lt;/iframe>
&lt;/div>
&lt;p>My work wasn&amp;rsquo;t limited to just the UI. As part of a small team, I contributed in many areas from backend development to gameplay programming. One standout feature for me was the &amp;ldquo;News&amp;rdquo; system. To keep players connected, I built a Node.js microservice allowing our Community Management team to post website updates that seamlessly appeared in-game.&lt;/p>
&lt;p>Ultimately, I made sure &amp;ldquo;Sandship&amp;rdquo; delivered a visually engaging and smooth player experience. Tackling these challenges was as rewarding as seeing players immersed in the world we built.&lt;/p>
&lt;p>&lt;img src="https://senpie.net/post/sandship/sandship-1.jpeg"
width="1920"
height="1080"
srcset="https://senpie.net/post/sandship/sandship-1_hu1f2ced91771ffa736a99e93a73a6b369_270530_480x0_resize_q75_box.jpeg 480w, https://senpie.net/post/sandship/sandship-1_hu1f2ced91771ffa736a99e93a73a6b369_270530_1024x0_resize_q75_box.jpeg 1024w"
loading="lazy"
alt="Outside view. You can see the Sandship itself and crafting buildings on top of it."
class="gallery-image"
data-flex-grow="177"
data-flex-basis="426px"
>&lt;img src="https://senpie.net/post/sandship/sandship-2.jpeg"
width="1920"
height="1080"
srcset="https://senpie.net/post/sandship/sandship-2_hu1f2ced91771ffa736a99e93a73a6b369_514812_480x0_resize_q75_box.jpeg 480w, https://senpie.net/post/sandship/sandship-2_hu1f2ced91771ffa736a99e93a73a6b369_514812_1024x0_resize_q75_box.jpeg 1024w"
loading="lazy"
alt="Inside a Sadnship building."
class="gallery-image"
data-flex-grow="177"
data-flex-basis="426px"
>&lt;/p></description></item><item><title>Search</title><link>https://senpie.net/page/search/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://senpie.net/page/search/</guid><description>&lt;!-- menu:
main:
weight: -60
params:
icon: search --></description></item></channel></rss>