• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Stellarium / stellarium / 4853788370

pending completion
4853788370

push

github

Alexander V. Wolf
Special patch for John Simple

3 of 3 new or added lines in 3 files covered. (100.0%)

14729 of 125046 relevant lines covered (11.78%)

20166.5 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/plugins/Scenery3d/src/ShaderManager.cpp
1
/*
2
 * Stellarium Scenery3d Plug-in
3
 *
4
 * Copyright (C) 2014 Simon Parzer, Peter Neubauer, Georg Zotti, Andrei Borza, Florian Schaukowitsch
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 2
9
 * of the License, or (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA  02110-1335, USA.
19
 */
20

21
#include "StelOpenGL.hpp"
22
#include "ShaderManager.hpp"
23
#include "StelFileMgr.hpp"
24

25
#include <QDir>
26
#include <QOpenGLShaderProgram>
27
#include <QCryptographicHash>
28
#include <QDebug>
29

30
Q_LOGGING_CATEGORY(shaderMgr, "stel.plugin.scenery3d.shadermgr")
×
31

32
ShaderMgr::t_UniformStrings ShaderMgr::uniformStrings;
33
ShaderMgr::t_FeatureFlagStrings ShaderMgr::featureFlagsStrings;
34

35
ShaderMgr::ShaderMgr()
×
36
{
37
        if(uniformStrings.size()==0)
×
38
        {
39
                //initialize the strings
40
                uniformStrings["u_mModelView"] = UNIFORM_MAT_MODELVIEW;
×
41
                uniformStrings["u_mProjection"] = UNIFORM_MAT_PROJECTION;
×
42
                uniformStrings["u_mMVP"] = UNIFORM_MAT_MVP;
×
43
                uniformStrings["u_mNormal"] = UNIFORM_MAT_NORMAL;
×
44
                uniformStrings["u_mShadow0"] = UNIFORM_MAT_SHADOW0;
×
45
                uniformStrings["u_mShadow1"] = UNIFORM_MAT_SHADOW1;
×
46
                uniformStrings["u_mShadow2"] = UNIFORM_MAT_SHADOW2;
×
47
                uniformStrings["u_mShadow3"] = UNIFORM_MAT_SHADOW3;
×
48
                uniformStrings["u_mCubeMVP"] = UNIFORM_MAT_CUBEMVP;
×
49
                uniformStrings["u_mCubeMVP[]"] = UNIFORM_MAT_CUBEMVP;
×
50
                uniformStrings["u_mCubeMVP[0]"] = UNIFORM_MAT_CUBEMVP;
×
51

52
                //textures
53
                uniformStrings["u_texDiffuse"] = UNIFORM_TEX_DIFFUSE;
×
54
                uniformStrings["u_texEmissive"] = UNIFORM_TEX_EMISSIVE;
×
55
                uniformStrings["u_texBump"] = UNIFORM_TEX_BUMP;
×
56
                uniformStrings["u_texHeight"] = UNIFORM_TEX_HEIGHT;
×
57
                uniformStrings["u_texShadow0"] = UNIFORM_TEX_SHADOW0;
×
58
                uniformStrings["u_texShadow1"] = UNIFORM_TEX_SHADOW1;
×
59
                uniformStrings["u_texShadow2"] = UNIFORM_TEX_SHADOW2;
×
60
                uniformStrings["u_texShadow3"] = UNIFORM_TEX_SHADOW3;
×
61

62
                //materials
63
                uniformStrings["u_vMatShininess"] = UNIFORM_MTL_SHININESS;
×
64
                uniformStrings["u_vMatAlpha"] = UNIFORM_MTL_ALPHA;
×
65

66
                //pre-modulated lighting (light * material)
67
                uniformStrings["u_vMixAmbient"] = UNIFORM_MIX_AMBIENT;
×
68
                uniformStrings["u_vMixDiffuse"] = UNIFORM_MIX_DIFFUSE;
×
69
                uniformStrings["u_vMixSpecular"] = UNIFORM_MIX_SPECULAR;
×
70
                uniformStrings["u_vMixTorchDiffuse"] = UNIFORM_MIX_TORCHDIFFUSE;
×
71
                uniformStrings["u_vMixEmissive"] = UNIFORM_MIX_EMISSIVE;
×
72

73
                //light
74
                uniformStrings["u_vLightDirectionView"] = UNIFORM_LIGHT_DIRECTION_VIEW;
×
75
                uniformStrings["u_fTorchAttenuation"] = UNIFORM_TORCH_ATTENUATION;
×
76

77
                //others
78
                uniformStrings["u_vColor"] = UNIFORM_VEC_COLOR;
×
79
                uniformStrings["u_vSplits"] = UNIFORM_VEC_SPLITDATA;
×
80
                uniformStrings["u_vLightOrthoScale"] = UNIFORM_VEC_LIGHTORTHOSCALE;
×
81
                uniformStrings["u_vLightOrthoScale[]"] = UNIFORM_VEC_LIGHTORTHOSCALE;
×
82
                uniformStrings["u_vLightOrthoScale[0]"] = UNIFORM_VEC_LIGHTORTHOSCALE;
×
83
                uniformStrings["u_fAlphaThresh"] = UNIFORM_FLOAT_ALPHA_THRESH;
×
84
        }
85
        if(featureFlagsStrings.size()==0)
×
86
        {
87
                featureFlagsStrings["TRANSFORM"] = TRANSFORM;
×
88
                featureFlagsStrings["SHADING"] = SHADING;
×
89
                featureFlagsStrings["PIXEL_LIGHTING"] = PIXEL_LIGHTING;
×
90
                featureFlagsStrings["SHADOWS"] = SHADOWS;
×
91
                featureFlagsStrings["BUMP"] = BUMP;
×
92
                featureFlagsStrings["HEIGHT"] = HEIGHT;
×
93
                featureFlagsStrings["ALPHATEST"] = ALPHATEST;
×
94
                featureFlagsStrings["SHADOW_FILTER"] = SHADOW_FILTER;
×
95
                featureFlagsStrings["SHADOW_FILTER_HQ"] = SHADOW_FILTER_HQ;
×
96
                featureFlagsStrings["MAT_SPECULAR"] = MAT_SPECULAR;
×
97
                featureFlagsStrings["MAT_DIFFUSETEX"] = MAT_DIFFUSETEX;
×
98
                featureFlagsStrings["MAT_EMISSIVETEX"] = MAT_EMISSIVETEX;
×
99
                featureFlagsStrings["GEOMETRY_SHADER"] = GEOMETRY_SHADER;
×
100
                featureFlagsStrings["CUBEMAP"] = CUBEMAP;
×
101
                featureFlagsStrings["BLENDING"] = BLENDING;
×
102
                featureFlagsStrings["TORCH"] = TORCH;
×
103
                featureFlagsStrings["DEBUG"] = DEBUG;
×
104
                featureFlagsStrings["PCSS"] = PCSS;
×
105
                featureFlagsStrings["SINGLE_SHADOW_FRUSTUM"] = SINGLE_SHADOW_FRUSTUM;
×
106
                featureFlagsStrings["OGL_ES2"] = OGL_ES2;
×
107
                featureFlagsStrings["HW_SHADOW_SAMPLERS"] = HW_SHADOW_SAMPLERS;
×
108
        }
109
}
×
110

111
ShaderMgr::~ShaderMgr()
×
112
{
113
        clearCache();
×
114
}
×
115

116
void ShaderMgr::clearCache()
×
117
{
118
        qCDebug(shaderMgr)<<"Clearing"<<m_shaderContentCache.size()<<"shaders";
×
119

120
        //iterate over the shaderContentCache - this contains the same amount of shaders as actually exist!
121
        //the shaderCache could contain duplicate entries
122
        for (auto* shader : m_shaderContentCache)
×
123
        {
124
                if (shader)
×
125
                        delete shader;
×
126
        }
127

128
        m_shaderCache.clear();
×
129
        m_uniformCache.clear();
×
130
        m_shaderContentCache.clear();
×
131
}
×
132

133
QOpenGLShaderProgram* ShaderMgr::findOrLoadShader(uint flags)
×
134
{
135
        auto it = m_shaderCache.find(flags);
×
136

137
        // This may also return Q_NULLPTR if the load failed.
138
        //We wait until user explicitly forces shader reload until we try again to avoid spamming errors.
139
        if(it!=m_shaderCache.end())
×
140
                return *it;
×
141

142
        //get shader file names
143
        QString vShaderFile = getVShaderName(flags);
×
144
        QString gShaderFile = getGShaderName(flags);
×
145
        QString fShaderFile = getFShaderName(flags);
×
146
        qCDebug(shaderMgr)<<"Loading Scenery3d shader: flags:"<<flags<<", vs:"<<vShaderFile<<", gs:"<<gShaderFile<<", fs:"<<fShaderFile<<"";
×
147

148
        //load shader files & preprocess
149
        QByteArray vShader,gShader,fShader;
×
150

151
        QOpenGLShaderProgram *prog = Q_NULLPTR;
×
152

153
        if(preprocessShader(vShaderFile,flags,vShader) &&
×
154
                        preprocessShader(gShaderFile,flags,gShader) &&
×
155
                        preprocessShader(fShaderFile,flags,fShader)
×
156
                        )
157
        {
158
                //check if this content-hash was already created for optimization
159
                //(so that shaders with different flags, but identical implementation use the same program)
160
                QCryptographicHash hash(QCryptographicHash::Sha256);
×
161
                hash.addData(vShader);
×
162
                hash.addData(gShader);
×
163
                hash.addData(fShader);
×
164

165
                QByteArray contentHash = hash.result();
×
166
                if(m_shaderContentCache.contains(contentHash))
×
167
                {
168
#ifndef NDEBUG
169
                        //qCDebug(shaderMgr)<<"Using existing shader with content-hash"<<contentHash.toHex();
170
#endif
171
                        prog = m_shaderContentCache[contentHash];
×
172
                }
173
                else
174
                {
175
                        //we have to compile the shader
176
                        prog = new QOpenGLShaderProgram();
×
177

178
                        if(!loadShader(*prog,vShader,gShader,fShader))
×
179
                        {
180
                                delete prog;
×
181
                                prog = Q_NULLPTR;
×
182
                                qCCritical(shaderMgr)<<"ERROR: Shader '"<<flags<<"' could not be compiled. Fix errors and reload shaders or restart program.";
×
183
                        }
184
#ifndef NDEBUG
185
                        else
186
                        {
187
                                //qCDebug(shaderMgr)<<"Shader '"<<flags<<"' created, content-hash"<<contentHash.toHex();
188
                        }
189
#endif
190
                        m_shaderContentCache[contentHash] = prog;
×
191
                }
192
        }
×
193
        else
194
        {
195
                qCCritical(shaderMgr)<<"ERROR: Shader '"<<flags<<"' could not be loaded/preprocessed.";
×
196
        }
197

198

199
        //may put null in cache on fail!
200
        m_shaderCache[flags] = prog;
×
201

202
        return prog;
×
203
}
×
204

205
QString ShaderMgr::getVShaderName(uint flags)
×
206
{
207
        if(flags & SHADING)
×
208
        {
209
                if (! (flags & PIXEL_LIGHTING ))
×
210
                        return "s3d_vertexlit.vert";
×
211
                else
212
                        return "s3d_pixellit.vert";
×
213
        }
214
        else if (flags & CUBEMAP)
×
215
        {
216
                return "s3d_cube.vert";
×
217
        }
218
        else if (flags & TRANSFORM)
×
219
        {
220
                return "s3d_transform.vert";
×
221
        }
222
        else if (flags & MAT_DIFFUSETEX)
×
223
        {
224
                return "s3d_texture.vert";
×
225
        }
226
        else if (flags & DEBUG)
×
227
        {
228
                return "s3d_debug.vert";
×
229
        }
230
        return QString();
×
231
}
232

233
QString ShaderMgr::getGShaderName(uint flags)
×
234
{
235
        if(flags & GEOMETRY_SHADER)
×
236
        {
237
                if(flags & PIXEL_LIGHTING)
×
238
                        return "s3d_pixellit.geom";
×
239
                else
240
                        return "s3d_vertexlit.geom";
×
241
        }
242
        return QString();
×
243
}
244

245
QString ShaderMgr::getFShaderName(uint flags)
×
246
{
247
        if(flags & SHADING)
×
248
        {
249
                if (! (flags & PIXEL_LIGHTING ))
×
250
                        return "s3d_vertexlit.frag";
×
251
                else
252
                {
253
                        if(flags & OGL_ES2)
×
254
                                return "s3d_pixellit_es.frag"; //for various reasons, ES version is separate
×
255
                        else
256
                                return "s3d_pixellit.frag";
×
257
                }
258
        }
259
        else if (flags & CUBEMAP)
×
260
        {
261
                return "s3d_cube.frag";
×
262
        }
263
        else if (flags & TRANSFORM)
×
264
        {
265
                //OGL ES2 always wants a fragment shader (at least on ANGLE)
266
                if((flags & ALPHATEST) || (flags & OGL_ES2))
×
267
                        return "s3d_transform.frag";
×
268
        }
269
        else if (flags == MAT_DIFFUSETEX)
×
270
        {
271
                return "s3d_texture.frag";
×
272
        }
273
        else if (flags & DEBUG)
×
274
        {
275
                return "s3d_debug.frag";
×
276
        }
277
        return QString();
×
278
}
279

280
bool ShaderMgr::preprocessShader(const QString &fileName, const uint flags, QByteArray &processedSource)
×
281
{
282
        if(fileName.isEmpty())
×
283
        {
284
                //no shader of this type
285
                return true;
×
286
        }
287

288
        QDir dir("data/shaders/");
×
289
        QString filePath = StelFileMgr::findFile(dir.filePath(fileName),StelFileMgr::File);
×
290

291
        //open and load file
292
        QFile file(filePath);
×
293
#ifndef NDEBUG
294
        //qCDebug(shaderMgr)<<"File path:"<<filePath;
295
#endif
296
        if(!file.open(QFile::ReadOnly))
×
297
        {
298
                qCCritical(shaderMgr)<<"Could not open file"<<filePath;
×
299
                return false;
×
300
        }
301

302
        processedSource.clear();
×
303
        processedSource.reserve(static_cast<int>(file.size()));
×
304

305
        //use a text stream for "parsing"
306
        QTextStream in(&file),lineStream;
×
307

308
        QString line,word;
×
309
        while(!in.atEnd())
×
310
        {
311
                line = in.readLine();
×
312
                lineStream.setString(&line,QIODevice::ReadOnly);
×
313

314
                QString write = line;
×
315

316
                //read first word
317
                lineStream>>word;
×
318
                if(word == "#define")
×
319
                {
320
                        //read second word
321
                        lineStream>>word;
×
322

323
                        //try to find it in our flags list
324
                        auto it = featureFlagsStrings.find(word);
×
325
                        if(it!=featureFlagsStrings.end())
×
326
                        {
327
                                bool val = it.value() & flags;
×
328
                                write = "#define " + word + (val?" 1":" 0");
×
329
#ifdef NDEBUG
330
                        }
331
#else
332
                                //output matches for debugging
333
                                //qCDebug(shaderMgr)<<"preprocess match: "<<line <<" --> "<<write;
334
                        }
335
                        else
336
                        {
337
                                //qCDebug(shaderMgr)<<"unknown define, ignoring: "<<line;
338
                        }
339
#endif
340
                }
341

342
                //write output
343
                processedSource.append(write.toUtf8());
×
344
                processedSource.append('\n');
×
345
        }
×
346
        return true;
×
347
}
×
348

349
bool ShaderMgr::loadShader(QOpenGLShaderProgram& program, const QByteArray& vShader, const QByteArray& gShader, const QByteArray& fShader)
×
350
{
351
        //clear old shader data, if exists
352
        program.removeAllShaders();
×
353

354
        if(!vShader.isEmpty())
×
355
        {
356
                const auto prefix = StelOpenGL::globalShaderPrefix(StelOpenGL::VERTEX_SHADER);
×
357
                if(!program.addShaderFromSourceCode(QOpenGLShader::Vertex, prefix + vShader))
×
358
                {
359
                        qCCritical(shaderMgr) << "Unable to compile vertex shader";
×
360
                        qCCritical(shaderMgr) << program.log();
×
361
                        return false;
×
362
                }
363
                else
364
                {
365
                        //TODO Qt wrapper does not seem to provide warnings (regardless of what its doc says)!
366
                        //Raise a bug with them or handle shader loading ourselves?
367
                        QString log = program.log().trimmed();
×
368
                        if(!log.isEmpty())
×
369
                        {
370
                                qCWarning(shaderMgr)<<"Vertex shader warnings:";
×
371
                                qCWarning(shaderMgr)<<log;
×
372
                        }
373
                }
×
374
        }
×
375

376
        if(!gShader.isEmpty())
×
377
        {
378
                if(!program.addShaderFromSourceCode(QOpenGLShader::Geometry,gShader))
×
379
                {
380
                        qCCritical(shaderMgr) << "Unable to compile geometry shader";
×
381
                        qCCritical(shaderMgr) << program.log();
×
382
                        return false;
×
383
                }
384
                else
385
                {
386
                        //TODO Qt wrapper does not seem to provide warnings (regardless of what its doc says)!
387
                        //Raise a bug with them or handle shader loading ourselves?
388
                        QString log = program.log().trimmed();
×
389
                        if(!log.isEmpty())
×
390
                        {
391
                                qCWarning(shaderMgr)<<"Geometry shader warnings:";
×
392
                                qCWarning(shaderMgr)<<log;
×
393
                        }
394
                }
×
395
        }
396

397
        if(!fShader.isEmpty())
×
398
        {
399
                const auto globalPrefix = StelOpenGL::globalShaderPrefix(StelOpenGL::FRAGMENT_SHADER);
×
400
                const bool prefixHasVersion = globalPrefix.contains("#version");
×
401
                const bool shaderHasVersion = fShader.contains("#version");
×
402
                QByteArray prefix;
×
403
                QByteArray finalShader = fShader;
×
404
                if(shaderHasVersion)
×
405
                {
406
                        const auto shaderVersionString = QString(fShader.simplified()).replace(QRegularExpression("^#version ([0-9]+)\\b.*"), "\\1");
×
407
                        bool svOK = false;
×
408
                        const auto shaderVersion = shaderVersionString.toInt(&svOK);
×
409

410
                        const auto prefixVersionString = QString(globalPrefix.simplified()).replace(QRegularExpression("^#version ([0-9]+)\\b.*"), "\\1");
×
411
                        bool pvOK = false;
×
412
                        const auto prefixVersion = prefixVersionString.toInt(&pvOK);
×
413

414
                        bool failed = false;
×
415
                        if(!svOK)
×
416
                        {
417
                                qCCritical(shaderMgr) << "Failed to get shader version, string:" << shaderVersionString;
×
418
                                failed = true;
×
419
                        }
420

421
                        if(prefixHasVersion && !pvOK)
×
422
                        {
423
                                qCCritical(shaderMgr) << "Failed to get prefix version, string:" << prefixVersionString;
×
424
                                failed = true;
×
425
                        }
426

427
                        if(failed) return false;
×
428

429
                        if(!prefixHasVersion)
×
430
                        {
431
                                prefix = QString("#version %1\n").arg(shaderVersion).toUtf8() + globalPrefix;
×
432
                        }
433
                        else if(shaderVersion > prefixVersion)
×
434
                        {
435
                                prefix = QString(globalPrefix).replace(QRegularExpression("(^|\n)(#version[ \t]+)([0-9]+)\\b"),
×
436
                                                                                                           QString("\\1\\2%1").arg(shaderVersion)).toUtf8();
×
437
                        }
438
                        else
439
                        {
440
                                prefix = globalPrefix;
×
441
                        }
442
                        finalShader = QString(fShader).replace(QRegularExpression("(^|\n)(#version[ \t]+)([0-9]+)\\b"), "//\\1\\2\\3").toUtf8();
×
443
                }
×
444
                else
445
                {
446
                        prefix = globalPrefix;
×
447
                }
448
                if(!program.addShaderFromSourceCode(QOpenGLShader::Fragment, prefix + finalShader))
×
449
                {
450
                        qCCritical(shaderMgr) << "Unable to compile fragment shader";
×
451
                        qCCritical(shaderMgr) << program.log();
×
452
                        return false;
×
453
                }
454
                else
455
                {
456
                        //TODO Qt wrapper does not seem to provide warnings (regardless of what its doc says)!
457
                        //Raise a bug with them or handle shader loading ourselves?
458
                        QString log = program.log().trimmed();
×
459
                        if(!log.isEmpty())
×
460
                        {
461
                                qCWarning(shaderMgr)<<"Fragment shader warnings:";
×
462
                                qCWarning(shaderMgr)<<log;
×
463
                        }
464
                }
×
465
        }
×
466

467
        //Set attribute locations to hardcoded locations.
468
        //This enables us to use a single VAO configuration for all shaders!
469
        program.bindAttributeLocation("a_vertex",StelOpenGLArray::ATTLOC_VERTEX);
×
470
        program.bindAttributeLocation("a_normal", StelOpenGLArray::ATTLOC_NORMAL);
×
471
        program.bindAttributeLocation("a_texcoord",StelOpenGLArray::ATTLOC_TEXCOORD);
×
472
        program.bindAttributeLocation("a_tangent",StelOpenGLArray::ATTLOC_TANGENT);
×
473
        program.bindAttributeLocation("a_bitangent",StelOpenGLArray::ATTLOC_BITANGENT);
×
474

475

476
        //link program
477
        if(!program.link())
×
478
        {
479
                qCCritical(shaderMgr)<<"[ShaderMgr] unable to link shader";
×
480
                qCCritical(shaderMgr)<<program.log();
×
481
                return false;
×
482
        }
483

484
        buildUniformCache(program);
×
485
        return true;
×
486
}
487

488
void ShaderMgr::buildUniformCache(QOpenGLShaderProgram &program)
×
489
{
490
        //this enumerates all available uniforms of this shader, and stores their locations in a map
491
        GLuint prog = program.programId();
×
492
        GLint numUniforms=0,bufSize;
×
493

494
        QOpenGLFunctions* gl = QOpenGLContext::currentContext()->functions();
×
495
        GL(gl->glGetProgramiv(prog, GL_ACTIVE_UNIFORMS, &numUniforms));
×
496
        GL(gl->glGetProgramiv(prog, GL_ACTIVE_UNIFORM_MAX_LENGTH, &bufSize));
×
497

498
        QByteArray buf(bufSize,'\0');
×
499
        GLsizei length;
500
        GLint size;
501
        GLenum type;
502

503
#ifndef NDEBUG
504
        //qCDebug(shaderMgr)<<"Shader has"<<numUniforms<<"uniforms";
505
#endif
506
        for(uint i =0;i<static_cast<GLuint>(numUniforms);++i)
×
507
        {
508
                GL(gl->glGetActiveUniform(prog,i,bufSize,&length,&size,&type,buf.data()));
×
509
                QString str(buf);
×
510
                str = str.left(str.indexOf(QChar(0x00)));
×
511

512
                GLint loc = program.uniformLocation(str);
×
513

514
                auto it = uniformStrings.find(str);
×
515

516
                // This may also return Q_NULLPTR if the load failed.
517
                //We wait until user explicitly forces shader reload until we try again to avoid spamming errors.
518
                if(it!=uniformStrings.end())
×
519
                {
520
                        //this is uniform we recognize
521
                        //need to get the uniforms location (!= index)
522
                        m_uniformCache[&program][*it] = static_cast<GLuint>(loc);
×
523
                        //output mapping for debugging
524
                        //qCDebug(shaderMgr)<<i<<loc<<str<<size<<type<<" mapped to "<<*it;
525
                }
526
                else
527
                {
528
                        qCWarning(shaderMgr)<<i<<loc<<str<<size<<type<<" --- unknown uniform! ---";
×
529
                }
530
        }
×
531
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc