# Behind the scene of Camera.ScreenToWorldpoint() in Unity 2D

2016-11-19 09:00:12 Coding C++ Unity

For some reason I need to compute Camera.main.ScreenToWorldpoint manually. So, I need to know how it works internally. Since in 2D we have $$P_s = (P_v.x * w, P_v.y * h)$$, it is enough to understand how ViewportToWorldpoint works.

In theory (CG basics): $$P_v = M_{proj} * M_{view} * P_w$$, thus

$$P_w = M_{view}^{-1} * M_{proj}^{-1} * P_v$$

However, in Unity, $$P_v$$ is in the range [0, 1] rather than [-1, 1], which means it is actually

$$P_v = M_{extra} * M_{proj} * M_{view} * P_w$$

and thus

$$P_w = M_{view}^{-1} * M_{proj}^{-1} * M_{extra}^{-1} * P_v$$

Where

M_extra = [1, 0, 0, 1]   inv(M_extra) = [1, 0, 0, -0.5]
[0, 1, 0, 1]                  [0, 1, 0, -0.5]
[0, 0, 1, 0]                  [0, 0, 1,  0  ]
[0, 0, 0, 2]                  [0, 0, 0,  0.5]

Note that we don’t care about Z since it is in 2D orthographic projection mode.

Now, in the language of Unity:

Initialize:

var magicMatrix = new Matrix4x4
{
m00 = 1,
m03 = -0.5f,
m11 = 1,
m13 = -0.5f,
m22 = 1,
m33 = 0.5f
};
_originalViewportToWorldMatrix = Camera.main.cameraToWorldMatrix*Camera.main.projectionMatrix.inverse*magicMatrix;

Compute:

private Vector3 OriginalScreenToWorld(float x, float y, float setZ)
{
var vec = _originalViewportToWorldMatrix*new Vector4(x/_width, y/_height, 0, 1);
return new Vector3(vec.x/vec.w, vec.y/vec.w, setZ);
}

# Record all cout output in C++

2016-03-27 17:41:32 Coding C++

æˆ‘åŽ»å¹´åšUROPçš„æ—¶å€™ï¼Œç”¨C++å†™äº†ä¸€ä¸ªæŽ§åˆ¶å°ç¨‹åºã€‚åŽæ¥å¸Œæœ›æŠŠæ•´ä¸ªè¿‡ç¨‹è‡ªåŠ¨åŒ–ï¼Œä¹Ÿå°±éœ€è¦å¯¼å‡ºç¨‹åºçš„æ‰€æœ‰è¾“å‡ºã€‚ç„¶è€Œè¿™äº›è¾“å‡ºæœ¬èº«æ˜¯åœ¨couté‡Œçš„ï¼Œæˆ‘ä¹Ÿä¸æƒ³å¼„å¾—å¤ªå¤æ‚ï¼ˆæ¯”å¦‚å†å†™ä¸ªç¨‹åºwrapåœ¨å¤–é¢ä»€ä¹ˆçš„ï¼‰ã€‚åŒæ—¶æˆ‘å¸Œæœ›è¿™ä¸ªè¿‡ç¨‹æ˜¯é€æ˜Žçš„â€”â€”æˆ‘ä¸éœ€è¦ä¿®æ”¹é‚£äº›ç”¨äº†cout<<çš„åœ°æ–¹ã€‚

äºŽæ˜¯å…ˆå†™ä¸€ä¸ªç”¨äºŽæŠŠè¾“å…¥ä¸€ä¸ªstreambufçš„å­—ç¬¦åˆ†é…åˆ°ä¸¤ä¸ªstreambufä¸­çš„dist_streambufï¼š

/*
dist_streambuf.h
Author: logchan
Created: 2015-07-29
A dist_streambuf object is used to distribute the output operations to two streambuf objects (primary and secondary).
It can be used in some tricks, such as recording cout.
*/
#ifndef _LOGCHAN_DIST_STREAMBUF_H
#define _LOGCHAN_DIST_STREAMBUF_H
#include <streambuf>
#include <locale>
#include <ios>

using std::streambuf;
using std::streamsize;
using std::streampos;
using std::streamoff;
using std::locale;
using std::ios_base;

class dist_streambuf : public streambuf
{
public:
dist_streambuf(streambuf* primary, streambuf* secondary);
streambuf* primary_buf();
streambuf* primary_buf(streambuf* newbuf);
streambuf* secondary_buf();
streambuf* secondary_buf(streambuf* newbuf);

protected:
// Locales
void imbue(const locale& loc);
// Buffer management and positioning
streambuf* setbuf(char* s, streamsize n);
streampos seekoff(streamoff off, ios_base::seekdir way, ios_base::openmode which = ios_base::in | ios_base::out);
streampos seekpos(streampos sp, ios_base::openmode which = ios_base::in | ios_base::out);
int sync();
// Input functions
// Not implemented. It does not make sense to "distribute input to" two streambufs.
// Output functions
streamsize xsputn(const char* s, streamsize n);
int overflow(int c = EOF);

private:
streambuf* buf1;
streambuf* buf2;
};

#endif //_LOGCHAN_DIST_STREAMBUF_H


ä»¥åŠå®žçŽ°çš„cppæ–‡ä»¶ï¼š

#include "dist_streambuf.h"
#include <streambuf>
#include <stdexcept>
using namespace std;

dist_streambuf::dist_streambuf(streambuf * primary, streambuf * secondary)
{
if (primary == nullptr)
{
throw invalid_argument("null primary buf is not allowed.");
}

buf1 = primary;
buf2 = secondary;
}

streambuf * dist_streambuf::primary_buf()
{
return buf1;
}

streambuf * dist_streambuf::primary_buf(streambuf * newbuf)
{
if (newbuf == nullptr)
{
throw invalid_argument("null primary buf is not allowed.");
}

streambuf* prevbuf = buf1;
buf1 = newbuf;
return prevbuf;
}

streambuf * dist_streambuf::secondary_buf()
{
return buf2;
}

streambuf * dist_streambuf::secondary_buf(streambuf * newbuf)
{
streambuf* prevbuf = buf2;
buf2 = newbuf;
return prevbuf;
}
streamsize dist_streambuf::xsputn(const char * s, streamsize n)
{
streamsize r = buf1->sputn(s, n);
if (buf2 != nullptr)
{
buf2->sputn(s, n);
}

return r;
}

int dist_streambuf::overflow(int c)
{
int r = buf1->sputc(c);
if (buf2 != nullptr)
{
buf2->sputc(c);
}

return r;
}

void dist_streambuf::imbue(const locale & loc)
{
buf1->pubimbue(loc);
if (buf2 != nullptr)
{
buf2->pubimbue(loc);
}
}

streambuf * dist_streambuf::setbuf(char * s, streamsize n)
{
streambuf* r = buf1->pubsetbuf(s, n);
if (buf2 != nullptr)
{
buf2->pubsetbuf(s, n);
}

return r;
}

streampos dist_streambuf::seekoff(streamoff off, ios_base::seekdir way, ios_base::openmode which)
{
streampos r = buf1->pubseekoff(off, way, which);
if (buf2 != nullptr)
{
buf2->pubseekoff(off, way, which);
}

return r;
}

streampos dist_streambuf::seekpos(streampos sp, ios_base::openmode which)
{
streampos r = buf1->pubseekpos(sp, which);
if (buf2 != nullptr)
{
buf2->pubseekpos(sp, which);
}

return r;
}

int dist_streambuf::sync()
{
int r = buf1->pubsync();
if (buf2 != nullptr)
{
buf2->pubsync();
}

return r;
}


dist_streambufå…è®¸buf2ä¸ºnullptrã€‚ä½¿ç”¨æ—¶ï¼Œé€šè¿‡coutæœ¬èº«çš„bufferå’Œä¸€ä¸ªsstreamçš„bufferæž„é€ ä¸€ä¸ªdist_streambufå¹¶ç”¨å…¶æ›¿æ¢coutåŽŸæ¥çš„bufferå°±å¯ä»¥äº†ï¼š

// initialize the logging of cout
auto preserved_cout_buf = cout.rdbuf();
auto logstream_buf = logstream.rdbuf();
dist_buf = new dist_streambuf(preserved_cout_buf, logstream_buf);
cout.rdbuf(dist_buf);


å¦‚æžœå¸Œæœ›ä¸´æ—¶å…³é—­åˆ†é…ï¼Œæ—¢å¯ä»¥æŠŠpreserved_cout_bufè¿˜ç»™coutï¼Œä¹Ÿå¯ä»¥ç”¨dist_buf->secondary_buf(nullptr);æ¥ä½¿logstream_bufä¸å†æŽ¥å—åˆ°æ–°çš„å­—ç¬¦ã€‚å½“ç„¶ï¼Œå¦‚æžœæƒ³åˆ†é…åˆ°æ›´å¤šçš„bufferï¼Œä¹Ÿå¯ä»¥æŠŠä¸€ä¸ªdist_streambufä½œä¸ºå¦ä¸€ä¸ªçš„primaryæˆ–è€…secondaryâ€¦â€¦è¿™ä¹ˆä¸€è¯´ï¼Œæž„é€ å™¨æ²¡æœ‰æ£€æŸ¥ä¼ å…¥çš„æ˜¯ä¸æ˜¯ç­‰äºŽthisä¼¼ä¹Žæœ‰ç‚¹å±é™©â€¦â€¦å¤±ç­–

å•Šï¼Œå¤šä¹ˆå¥½ç”¨çš„ä¸œè¥¿ã€‚